@phreshos/core 0.1.0

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.
Files changed (53) hide show
  1. package/README.md +159 -0
  2. package/dist/askable.d.ts +22 -0
  3. package/dist/askable.js +0 -0
  4. package/dist/channel.d.ts +20 -0
  5. package/dist/channel.js +0 -0
  6. package/dist/client.d.ts +19 -0
  7. package/dist/client.js +7 -0
  8. package/dist/config.d.ts +81 -0
  9. package/dist/config.js +9 -0
  10. package/dist/endpoint.d.ts +70 -0
  11. package/dist/endpoint.js +4 -0
  12. package/dist/launch.d.ts +35 -0
  13. package/dist/launch.js +2 -0
  14. package/dist/main.d.ts +16 -0
  15. package/dist/main.js +16 -0
  16. package/dist/outcome.d.ts +8 -0
  17. package/dist/outcome.js +0 -0
  18. package/dist/process.d.ts +58 -0
  19. package/dist/process.js +4 -0
  20. package/dist/program.d.ts +96 -0
  21. package/dist/program.js +4 -0
  22. package/dist/publishable.d.ts +21 -0
  23. package/dist/publishable.js +0 -0
  24. package/dist/served-file.d.ts +11 -0
  25. package/dist/served-file.js +0 -0
  26. package/dist/server.d.ts +42 -0
  27. package/dist/server.js +7 -0
  28. package/dist/sql.d.ts +17 -0
  29. package/dist/sql.js +0 -0
  30. package/dist/storage.d.ts +49 -0
  31. package/dist/storage.js +0 -0
  32. package/dist/subscribable.d.ts +72 -0
  33. package/dist/subscribable.js +0 -0
  34. package/dist/window.d.ts +60 -0
  35. package/dist/window.js +4 -0
  36. package/package.json +27 -0
  37. package/source/askable.ts +26 -0
  38. package/source/channel.ts +28 -0
  39. package/source/client.ts +29 -0
  40. package/source/config.ts +104 -0
  41. package/source/endpoint.ts +95 -0
  42. package/source/launch.ts +43 -0
  43. package/source/main.ts +65 -0
  44. package/source/outcome.ts +4 -0
  45. package/source/process.ts +77 -0
  46. package/source/program.ts +130 -0
  47. package/source/publishable.ts +33 -0
  48. package/source/served-file.ts +14 -0
  49. package/source/server.ts +58 -0
  50. package/source/sql.ts +36 -0
  51. package/source/storage.ts +66 -0
  52. package/source/subscribable.ts +94 -0
  53. package/source/window.ts +82 -0
@@ -0,0 +1,21 @@
1
+ import type { EventMessage, EventName } from "./subscribable.js";
2
+ type OpenEvent = string & {};
3
+ type AvailableEvent<Events extends object, Fallback> = EventName<Events> | ([Fallback] extends [never] ? never : OpenEvent);
4
+ type CompatibleEvent<Events extends object, Fallback, Payload> = {
5
+ [Event in EventName<Events>]: Payload extends Events[Event] ? Event : never;
6
+ }[EventName<Events>] | ([Fallback] extends [never] ? never : Payload extends Fallback ? OpenEvent : never);
7
+ interface Publish<Events extends object, Fallback> {
8
+ <Payload, Event extends CompatibleEvent<Events, Fallback, Payload> = CompatibleEvent<Events, Fallback, Payload>>(event: Event, payload: Payload): void;
9
+ <Event extends AvailableEvent<Events, Fallback>>(event: Event, payload: EventMessage<Events, Fallback, Event>): void;
10
+ }
11
+ /**
12
+ * The independent capability to publish one payload to a named event.
13
+ *
14
+ * Publishing is synchronous and fire-and-forget. An unavailable destination
15
+ * silently drops the publication; no delivery result is produced.
16
+ */
17
+ export interface Publishable<Events extends object = {}, Fallback = unknown> {
18
+ /** Publishes one payload to one named event on this target. */
19
+ publish: Publish<Events, Fallback>;
20
+ }
21
+ export {};
File without changes
@@ -0,0 +1,11 @@
1
+ /** Description of a value stored as a publicly reachable file. */
2
+ export type ServedFile = Readonly<{
3
+ /** Generated filename beneath the public served-files route. */
4
+ file: string;
5
+ /** Detected media type, or `null` when no type is known. */
6
+ type: string | null;
7
+ /** Stored size in bytes. */
8
+ size: number;
9
+ /** Storage time as Unix milliseconds. */
10
+ time: number;
11
+ }>;
File without changes
@@ -0,0 +1,42 @@
1
+ import type { Askable } from "./askable.js";
2
+ import { Endpoint, type EndpointTraffic } from "./endpoint.js";
3
+ import type { Outcome } from "./outcome.js";
4
+ import type { Cleanup } from "./subscribable.js";
5
+ /** One answer sent by this Server to the Endpoint that asked. */
6
+ export type AnswerMessage<Result = unknown, To = Endpoint> = Readonly<{
7
+ /** Destination Endpoint, or `null` when its identity is outside this boundary. */
8
+ to: To;
9
+ /** Transport-neutral success or failure returned by the answerer. */
10
+ outcome: Outcome<Result>;
11
+ }>;
12
+ /** One observed answer sent by this Server. */
13
+ export type AnswerCapture<Result = unknown, To = Endpoint> = Readonly<{
14
+ /** Event originally addressed by the question. */
15
+ event: string;
16
+ /** Correlation identity shared with the original question. */
17
+ questionId: string;
18
+ /** Answer destination and outcome. */
19
+ message: AnswerMessage<Result, To>;
20
+ }>;
21
+ /** A callback that observes answers sent by this Server. */
22
+ export type AnswerObserver<Result = unknown, To = Endpoint> = (capture: AnswerCapture<Result, To>) => unknown;
23
+ /** Broad communication originating from one Server, including its answers. */
24
+ export interface ServerTraffic<Events extends object = {}, To = Endpoint, AskTo = Server> extends EndpointTraffic<Events, To, AskTo> {
25
+ /** Observes answers originating from this Server. */
26
+ observeAnswers<Result = unknown>(observer: AnswerObserver<Result, To>): Cleanup;
27
+ }
28
+ /** The server Endpoint of a Process. */
29
+ export declare class Server<Events extends object = {}> extends Endpoint<Events> {
30
+ constructor();
31
+ }
32
+ export interface Server<Events extends object = {}> extends Askable {
33
+ /** Broad communication originating from this Server. */
34
+ readonly traffic: ServerTraffic<Events>;
35
+ /**
36
+ * Waits until a Server incarnation is ready.
37
+ *
38
+ * Temporary absence remains waitable when the Program declares a Server.
39
+ * The SDK uses its ten-second deadline unless one is supplied.
40
+ */
41
+ waitReady(timeout?: number): Promise<void>;
42
+ }
package/dist/server.js ADDED
@@ -0,0 +1,7 @@
1
+ import { Endpoint } from "./endpoint.js";
2
+ /** The server Endpoint of a Process. */
3
+ export class Server extends Endpoint {
4
+ constructor() {
5
+ super();
6
+ }
7
+ }
package/dist/sql.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ /** SQL access to a Program-owned database or its read-only logs. */
2
+ export interface ProgramSql {
3
+ /** Executes one statement; template interpolations become bound values. */
4
+ query<Row = Record<string, unknown>>(statement: TemplateStringsArray, ...values: unknown[]): Promise<Row[]>;
5
+ /** Executes one statement with optional bound values. */
6
+ query<Row = Record<string, unknown>>(statement: string, values?: unknown[]): Promise<Row[]>;
7
+ }
8
+ /** One captured line from a Program endpoint. */
9
+ export type LogRecord = Readonly<{
10
+ createdAt: number;
11
+ process: string;
12
+ source: LogSource;
13
+ kind: LogKind;
14
+ content: string;
15
+ }>;
16
+ export type LogSource = "client" | "server";
17
+ export type LogKind = "debug" | "log" | "info" | "warn" | "error" | "stdout" | "stderr" | "exit" | (string & {});
package/dist/sql.js ADDED
File without changes
@@ -0,0 +1,49 @@
1
+ /** Metadata returned for an entry in Program-owned filesystem storage. */
2
+ export type EntryStat = FileStat | DirectoryStat | OtherStat;
3
+ export type FileStat = Readonly<{
4
+ kind: "file";
5
+ size: number;
6
+ modifiedAt: number;
7
+ }>;
8
+ export type DirectoryStat = Readonly<{
9
+ kind: "directory";
10
+ modifiedAt: number;
11
+ }>;
12
+ export type OtherStat = Readonly<{
13
+ kind: "other";
14
+ modifiedAt: number;
15
+ }>;
16
+ /** Filesystem-like storage owned by one Program. */
17
+ export interface ProgramArea {
18
+ /** Reads one file as bytes. */
19
+ bytes(...path: string[]): Promise<Uint8Array>;
20
+ /** Reads one UTF-8 text file. */
21
+ text(...path: string[]): Promise<string>;
22
+ /** Reads and parses one JSON file. */
23
+ json<Value = unknown>(...path: string[]): Promise<Value>;
24
+ /** Opens one file as a byte stream. */
25
+ stream(...path: string[]): Promise<ReadableStream<Uint8Array>>;
26
+ /** Atomically writes one supported value, including a byte stream. */
27
+ write(...arguments_: [...path: string[], value: unknown]): Promise<void>;
28
+ /** Returns entry metadata, or `null` when the entry does not exist. */
29
+ stat(...path: string[]): Promise<EntryStat | null>;
30
+ /** Lists the sorted names immediately inside one directory. */
31
+ list(...path: string[]): Promise<string[]>;
32
+ /** Recursively removes an entry. A missing entry is accepted. */
33
+ delete(...path: string[]): Promise<void>;
34
+ /** Removes every entry while preserving the area itself. */
35
+ clear(): Promise<void>;
36
+ }
37
+ /** Persistent key-value storage owned by one Program. */
38
+ export interface ProgramStore {
39
+ /** Returns a value, or `undefined` when its key is absent or expired. */
40
+ get<Value = unknown>(key: string): Promise<Value | undefined>;
41
+ /** Stores a value and optionally expires it after `ttl` milliseconds. */
42
+ set<Value>(key: string, value: Value, ttl?: number): Promise<boolean>;
43
+ /** Deletes one key or several keys and reports whether anything changed. */
44
+ delete(key: string | string[]): Promise<boolean>;
45
+ /** Returns whether a non-expired value exists for one key. */
46
+ has(key: string): Promise<boolean>;
47
+ /** Deletes every value in this Program's store. */
48
+ clear(): Promise<void>;
49
+ }
File without changes
@@ -0,0 +1,72 @@
1
+ /** The unconstrained message received by a general Subscribable. */
2
+ export type Message = unknown;
3
+ /** The message observed for one event on a Subscribable. */
4
+ export type Capture<Event extends string = string, Received = Message> = Readonly<{
5
+ /** Name under which the message was published. */
6
+ event: Event;
7
+ /** Message delivered by the observed target. */
8
+ message: Received;
9
+ }>;
10
+ /** The string event names declared by an event map. */
11
+ export type EventName<Events extends object> = Extract<keyof Events, string>;
12
+ /** The message declared for one event name. */
13
+ export type EventMessage<Events extends object, Fallback, Event extends string> = Event extends EventName<Events> ? Events[Event] : Fallback;
14
+ /** The correlated union observed across every event declared by a target. */
15
+ export type Captures<Events extends object, Fallback = never> = {
16
+ [Event in EventName<Events>]: Capture<Event, Events[Event]>;
17
+ }[EventName<Events>] | ([Fallback] extends [never] ? never : Capture<string, Fallback>);
18
+ /** A callback for one subscribed message. Its return value is not communication. */
19
+ export type EventSubscriber<Message> = (message: Message) => unknown;
20
+ /** A callback for an observed event and its message. */
21
+ export type EventObserver<Events extends object, Fallback = never> = (capture: Captures<Events, Fallback>) => unknown;
22
+ /** Removes exactly one persistent registration. Safe to call more than once. */
23
+ export type Cleanup = () => void;
24
+ /** Options controlling one asynchronous event iterator. */
25
+ export type EventOptions = Readonly<{
26
+ /** Maximum queued messages. Defaults to `64`; `Infinity` removes the bound. */
27
+ capacity?: number;
28
+ /** Aborts iteration and removes its temporary boundary registration. */
29
+ signal?: AbortSignal;
30
+ }>;
31
+ type OpenEvent = string & {};
32
+ type AvailableEvent<Events extends object, Fallback> = EventName<Events> | ([Fallback] extends [never] ? never : OpenEvent);
33
+ type CompatibleEvent<Events extends object, Fallback, Narrowed> = {
34
+ [Event in EventName<Events>]: Narrowed extends Events[Event] ? Event : never;
35
+ }[EventName<Events>] | ([Fallback] extends [never] ? never : Narrowed extends Fallback ? OpenEvent : never);
36
+ interface Subscribe<Events extends object, Fallback> {
37
+ <Narrowed>(event: CompatibleEvent<Events, Fallback, Narrowed>, subscriber: EventSubscriber<Narrowed>): Cleanup;
38
+ <Event extends AvailableEvent<Events, Fallback>>(event: Event, subscriber: EventSubscriber<EventMessage<Events, Fallback, Event>>): Cleanup;
39
+ }
40
+ interface WaitFor<Events extends object, Fallback> {
41
+ <Narrowed>(event: CompatibleEvent<Events, Fallback, Narrowed>, timeout?: number): Promise<Narrowed>;
42
+ <Event extends AvailableEvent<Events, Fallback>>(event: Event, timeout?: number): Promise<EventMessage<Events, Fallback, Event>>;
43
+ }
44
+ interface EventStream<Events extends object, Fallback> {
45
+ <Narrowed>(event: CompatibleEvent<Events, Fallback, Narrowed>, options?: EventOptions): AsyncIterableIterator<Narrowed>;
46
+ <Event extends AvailableEvent<Events, Fallback>>(event: Event, options?: EventOptions): AsyncIterableIterator<EventMessage<Events, Fallback, Event>>;
47
+ }
48
+ interface Observe<Events extends object, Fallback> {
49
+ (observer: EventObserver<Events, Fallback>): Cleanup;
50
+ }
51
+ /**
52
+ * The common receiving capability supplied by environment SDKs.
53
+ *
54
+ * Persistent registrations return their sole cleanup function. A registration
55
+ * may remain active across temporary endpoint absence and is removed only by
56
+ * that cleanup or by destruction of its owning boundary.
57
+ *
58
+ * `waitFor()` uses the SDK's ten-second deadline unless one is supplied.
59
+ * `events()` is long-lived instead: it ends when iteration is closed, its
60
+ * signal aborts, or the boundary proves that future delivery is impossible.
61
+ */
62
+ export interface Subscribable<Events extends object = {}, Fallback = unknown> {
63
+ /** Registers one persistent named-event subscription. */
64
+ subscribe: Subscribe<Events, Fallback>;
65
+ /** Waits for the next matching message. */
66
+ waitFor: WaitFor<Events, Fallback>;
67
+ /** Iterates matching messages until closed, aborted, or impossible. */
68
+ events: EventStream<Events, Fallback>;
69
+ /** Registers one persistent observer across every event on this target. */
70
+ observe: Observe<Events, Fallback>;
71
+ }
72
+ export {};
File without changes
@@ -0,0 +1,60 @@
1
+ import type { Client } from "./client.js";
2
+ import type { Layer, Position, Size } from "./launch.js";
3
+ import type { Subscribable } from "./subscribable.js";
4
+ /** Events emitted when authoritative Window state changes. */
5
+ export type WindowEvents = {
6
+ move: Position;
7
+ resize: Size;
8
+ minimize: boolean;
9
+ changeTitle: string;
10
+ front: boolean;
11
+ };
12
+ /** Current authoritative Window state. */
13
+ export type WindowState = Readonly<{
14
+ /** Current title. */
15
+ title: string;
16
+ /** Current top-left position. */
17
+ position: Position;
18
+ /** Current width and height. */
19
+ size: Size;
20
+ /** Whether the Window is minimized. */
21
+ minimized: boolean;
22
+ /** Whether the Window is frontmost in its layer. */
23
+ front: boolean;
24
+ /** Structurally isolated desktop layer containing the Window. */
25
+ layer: Layer;
26
+ /** Current page beneath the declared Client location. */
27
+ location: string;
28
+ }>;
29
+ /** The Window owned by one live Client. */
30
+ export declare class Window<Events extends object = {}> {
31
+ constructor();
32
+ }
33
+ export interface Window<Events extends object = {}> extends Subscribable<WindowEvents & Events, never> {
34
+ /** Returns the Client that owns this Window. */
35
+ client(): Client;
36
+ /** Returns the current title. */
37
+ title(): Promise<string>;
38
+ /** Returns the current top-left position. */
39
+ position(): Promise<Position>;
40
+ /** Returns the current width and height. */
41
+ size(): Promise<Size>;
42
+ /** Returns whether the Window is minimized. */
43
+ minimized(): Promise<boolean>;
44
+ /** Returns whether the Window is frontmost in its layer. */
45
+ front(): Promise<boolean>;
46
+ /** Returns the structurally isolated desktop layer containing the Window. */
47
+ layer(): Promise<Layer>;
48
+ /** Returns the current page rooted beneath the declared Client location. */
49
+ location(): Promise<string>;
50
+ /** Moves the authoritative Window. */
51
+ move(position: Position): Promise<void>;
52
+ /** Resizes the authoritative Window. */
53
+ resize(size: Size): Promise<void>;
54
+ /** Changes whether the Window is minimized. */
55
+ minimize(minimized?: boolean): Promise<void>;
56
+ /** Changes the Window title. */
57
+ changeTitle(title: string): Promise<void>;
58
+ /** Brings the Window to the front of its own layer. */
59
+ raise(): Promise<void>;
60
+ }
package/dist/window.js ADDED
@@ -0,0 +1,4 @@
1
+ /** The Window owned by one live Client. */
2
+ export class Window {
3
+ constructor() { }
4
+ }
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@phreshos/core",
3
+ "version": "0.1.0",
4
+ "description": "Shared domain contracts for Program SDKs.",
5
+ "type": "module",
6
+ "main": "dist/main.js",
7
+ "types": "source/main.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./source/main.ts",
11
+ "default": "./dist/main.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "source",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "check": "tsc --noEmit",
21
+ "build": "tsc --noEmit false --outDir dist --rootDir source",
22
+ "prepack": "node --run build"
23
+ },
24
+ "devDependencies": {
25
+ "typescript": "^6.0.3"
26
+ }
27
+ }
@@ -0,0 +1,26 @@
1
+ import type { Publishable } from "./publishable.js"
2
+
3
+ /** An immutable Askable view using one caller-selected deadline. */
4
+ export interface TimedAskable {
5
+ /** Sends one question and waits within the selected deadline. */
6
+ ask<Answer = unknown, Payload = unknown>(event: string, payload: Payload): Promise<Answer>
7
+ }
8
+
9
+ /** A publishing target that can also receive a question and return an answer. */
10
+ export interface Askable<Events extends object = {}, Fallback = unknown>
11
+ extends Publishable<Events, Fallback> {
12
+ /**
13
+ * Sends one question payload to this target and waits for its answer.
14
+ *
15
+ * The SDK uses a ten-second deadline by default. It sends the question only
16
+ * after the current Server incarnation becomes ready. The operation rejects
17
+ * immediately when that Server is absent, and also rejects if the incarnation
18
+ * stops before readiness or before answering. One SDK deadline covers both
19
+ * readiness and the answer. The boundary cannot infer whether an answerer
20
+ * exists, so a ready unanswered question waits for that deadline.
21
+ */
22
+ ask<Answer = unknown, Payload = unknown>(event: string, payload: Payload): Promise<Answer>
23
+
24
+ /** Returns an immutable view whose `ask()` uses this deadline in milliseconds. */
25
+ timeout(milliseconds: number): TimedAskable
26
+ }
@@ -0,0 +1,28 @@
1
+ import type { Endpoint } from "./endpoint.js"
2
+ import type { Captures, Subscribable } from "./subscribable.js"
3
+
4
+ /** One application value arriving through the current Endpoint's Channel. */
5
+ export type ChannelMessage<Payload = unknown, From = Endpoint> = Readonly<{
6
+ /** Endpoint that sent the message, or `null` when its identity is outside this boundary. */
7
+ from: From
8
+
9
+ /** Single value supplied by the publisher. */
10
+ payload: Payload
11
+ }>
12
+
13
+ /** Applies the sender envelope to every application event accepted here. */
14
+ export type ChannelEvents<Events extends object, From = Endpoint> = {
15
+ readonly [Event in keyof Events]: ChannelMessage<Events[Event], From>
16
+ }
17
+
18
+ type ChannelFallback<Events extends object, From> = keyof Events extends never
19
+ ? ChannelMessage<unknown, From>
20
+ : never
21
+
22
+ /** Every application event observable through a Channel. */
23
+ export type ChannelCapture<Events extends object = {}, From = Endpoint> =
24
+ Captures<ChannelEvents<Events, From>, ChannelFallback<Events, From>>
25
+
26
+ /** Events explicitly accepted by the current Endpoint. */
27
+ export interface Channel<Events extends object = {}, From = Endpoint>
28
+ extends Subscribable<ChannelEvents<Events, From>, ChannelFallback<Events, From>> {}
@@ -0,0 +1,29 @@
1
+ import { Endpoint, type EndpointTraffic } from "./endpoint.js"
2
+ import type { LaunchClient } from "./launch.js"
3
+ import type { Server } from "./server.js"
4
+ import type { Window } from "./window.js"
5
+
6
+ /** Broad communication originating from one Client. */
7
+ export interface ClientTraffic<
8
+ Events extends object = {},
9
+ To = Endpoint,
10
+ AskTo = Server
11
+ > extends EndpointTraffic<Events, To, AskTo> {}
12
+
13
+ /** The client Endpoint of a Process. */
14
+ export class Client<Events extends object = {}> extends Endpoint<Events> {
15
+ public constructor() {
16
+ super()
17
+ }
18
+ }
19
+
20
+ export interface Client<Events extends object = {}> {
21
+ /** Broad communication originating from this Client. */
22
+ readonly traffic: ClientTraffic<Events>
23
+
24
+ /** Starts a fresh Client and Window using optional Process-local overrides. */
25
+ start(overrides?: LaunchClient): Promise<void>
26
+
27
+ /** Returns the Window owned by this live Client. */
28
+ window(): Promise<Window>
29
+ }
@@ -0,0 +1,104 @@
1
+ import type { Layer, Position, Size } from "./launch.js"
2
+
3
+ /** Development settings for a Program's Server. */
4
+ export type ServerDevelopment = Readonly<{
5
+ /** Command that starts the development Server from the project directory. */
6
+ startCommand: string
7
+ }>
8
+
9
+ /** Development settings for a Program's Client. */
10
+ export type ClientDevelopment = Readonly<{
11
+ /** HTTP(S) URL served by the Client's development server. */
12
+ url: string
13
+
14
+ /** Optional command that starts the Client's development server. */
15
+ startCommand?: string
16
+ }>
17
+
18
+ /** Authoring declaration for a Program's Server. */
19
+ export type ServerConfig = Readonly<{
20
+ /** Production directory containing the Server. */
21
+ location: string
22
+
23
+ /** Whether a default Process starts its Server. Defaults to `true`. */
24
+ start?: boolean
25
+
26
+ /** Optional preparation command run from {@link location} while installing. */
27
+ installCommand?: string
28
+
29
+ /** Command that starts the production Server from {@link location}. */
30
+ startCommand: string
31
+
32
+ /** Settings used only by the development command. */
33
+ development?: ServerDevelopment
34
+ }>
35
+
36
+ /** Authoring declaration for a Program's Client and initial Window. */
37
+ export type ClientConfig = Readonly<{
38
+ /** Production directory containing the Client and its `index.html`. */
39
+ location: string
40
+
41
+ /** Whether a default Process starts its Client. Defaults to `true`. */
42
+ start?: boolean
43
+
44
+ /** Initial Window title. Defaults to the Program name. */
45
+ title?: string
46
+
47
+ /** Initial Window size. */
48
+ size?: Size
49
+
50
+ /** Initial Window position. */
51
+ position?: Position
52
+
53
+ /** Structurally isolated desktop layer containing the Window. */
54
+ layer?: Layer
55
+
56
+ /** Whether the Window initially opens minimized. */
57
+ minimize?: boolean
58
+
59
+ /** Settings used only by the development command. */
60
+ development?: ClientDevelopment
61
+ }>
62
+
63
+ type Description = Readonly<{
64
+ /** Stable public identity written in kebab-case. */
65
+ identity: string
66
+
67
+ /** Human-readable Program name. Defaults to {@link identity}. */
68
+ name?: string
69
+
70
+ /** Program version shown to people and included in packages. */
71
+ version?: string
72
+
73
+ /** Short human-readable explanation of what the Program does. */
74
+ description?: string
75
+
76
+ /** Markdown file that officially introduces the Program's API. */
77
+ apiDocs?: string
78
+
79
+ /** Directory containing the Program's sized icons. */
80
+ icons?: string
81
+
82
+ /** Command run before production start, installation, and packaging. */
83
+ buildCommand?: string
84
+ }>
85
+
86
+ /**
87
+ * The authoring description read from `phresh.config.ts`.
88
+ *
89
+ * A Program must declare a Server, a Client, or both.
90
+ */
91
+ export type Config = Description & (
92
+ | Readonly<{ server: ServerConfig, client?: ClientConfig }>
93
+ | Readonly<{ server?: ServerConfig, client: ClientConfig }>
94
+ )
95
+
96
+ /**
97
+ * Defines a Program authoring description with contextual typing.
98
+ *
99
+ * This helper performs no work and returns the supplied description unchanged.
100
+ * The CLI validates and derives it for development, production, or packaging.
101
+ */
102
+ export function defineConfig<const Description extends Config>(config: Description): Description {
103
+ return config
104
+ }
@@ -0,0 +1,95 @@
1
+ import type { Process } from "./process.js"
2
+ import type { Publishable } from "./publishable.js"
3
+ import type { Server } from "./server.js"
4
+ import type { Captures, Cleanup, Subscribable } from "./subscribable.js"
5
+
6
+ /** One application value observed in traffic originating from an Endpoint. */
7
+ export type TrafficMessage<Payload = unknown, To = Endpoint> = Readonly<{
8
+ /** Destination Endpoint, or `null` when its identity is outside this boundary. */
9
+ to: To
10
+
11
+ /** Single value supplied by the publisher. */
12
+ payload: Payload
13
+ }>
14
+
15
+ /** Applies destination metadata to every observed ordinary event. */
16
+ export type TrafficEvents<Events extends object, To = Endpoint> = {
17
+ readonly [Event in keyof Events]: TrafficMessage<Events[Event], To>
18
+ }
19
+
20
+ type TrafficFallback<Events extends object, To> = keyof Events extends never
21
+ ? TrafficMessage<unknown, To>
22
+ : never
23
+
24
+ /** One question sent by this Endpoint to a Server. */
25
+ export type AskMessage<Payload = unknown, To = Server> = Readonly<{
26
+ /** Destination Server, or `null` when its identity is outside this boundary. */
27
+ to: To
28
+
29
+ /** Single question value supplied by the asker. */
30
+ payload: Payload
31
+ }>
32
+
33
+ /** One observed question sent by this Endpoint. */
34
+ export type AskCapture<Payload = unknown, To = Server> = Readonly<{
35
+ /** Event addressed by the question. */
36
+ event: string
37
+
38
+ /** Correlation identity shared with the eventual answer. */
39
+ questionId: string
40
+
41
+ /** Question destination and payload. */
42
+ message: AskMessage<Payload, To>
43
+ }>
44
+
45
+ /** A callback that observes questions sent by this Endpoint. */
46
+ export type AskObserver<Payload = unknown, To = Server> = (capture: AskCapture<Payload, To>) => unknown
47
+
48
+ /** Every ordinary publication observable in traffic from one Endpoint. */
49
+ export type TrafficCapture<Events extends object = {}, To = Endpoint> =
50
+ Captures<TrafficEvents<Events, To>, TrafficFallback<Events, To>>
51
+
52
+ /** Broad communication originating from one Endpoint, regardless of destination. */
53
+ export interface EndpointTraffic<
54
+ Events extends object = {},
55
+ To = Endpoint,
56
+ AskTo = Server
57
+ > extends Subscribable<TrafficEvents<Events, To>, TrafficFallback<Events, To>> {
58
+ /** Observes questions originating from this Endpoint. */
59
+ observeAsks<Payload = unknown>(observer: AskObserver<Payload, AskTo>): Cleanup
60
+ }
61
+
62
+ /** The shared Process endpoint represented by Server and Client. */
63
+ export class Endpoint<Events extends object = {}> {
64
+ public constructor() {}
65
+ }
66
+
67
+ export interface Endpoint<Events extends object = {}>
68
+ extends Publishable {
69
+ /** Broad communication originating from this Endpoint. */
70
+ readonly traffic: EndpointTraffic<Events>
71
+
72
+ /** Returns the Process that owns this Endpoint. */
73
+ process(): Process
74
+
75
+ /** Returns whether this Endpoint currently has a live incarnation. */
76
+ exists(): Promise<boolean>
77
+
78
+ /**
79
+ * Starts a fresh incarnation without waiting for Server readiness.
80
+ *
81
+ * Rejects when the Process is gone or inaccessible, the Program did not
82
+ * declare this endpoint kind, the Endpoint is already starting or live, or
83
+ * creation fails.
84
+ */
85
+ start(): Promise<void>
86
+
87
+ /**
88
+ * Stops the current incarnation and destroys its boundary-owned resources.
89
+ *
90
+ * Rejects when the Process is gone or inaccessible, the Endpoint is already
91
+ * stopping or absent, this is the Process's final live Endpoint, or stopping
92
+ * fails. Stopping never exits the Process implicitly.
93
+ */
94
+ stop(): Promise<void>
95
+ }
@@ -0,0 +1,43 @@
1
+ /** A pixel count or a relative linear expression. */
2
+ export type Value = number | string
3
+
4
+ /** A Window's top-left position. */
5
+ export type Position = Readonly<{
6
+ /** Horizontal position. */
7
+ x: Value
8
+
9
+ /** Vertical position. */
10
+ y: Value
11
+ }>
12
+
13
+ /** A Window's width and height. */
14
+ export type Size = Readonly<{
15
+ /** Window width. */
16
+ width: Value
17
+
18
+ /** Window height. */
19
+ height: Value
20
+ }>
21
+
22
+ /** A structurally isolated desktop layer. */
23
+ export type Layer = "window" | "under" | "over"
24
+
25
+ /** Every structurally isolated desktop layer. */
26
+ export const layers: readonly Layer[] = ["window", "under", "over"]
27
+
28
+ /** Per-Process overrides used when starting a Client and its Window. */
29
+ export type LaunchClient = Readonly<{
30
+ size?: Size
31
+ position?: Position
32
+ layer?: Layer
33
+ location?: string
34
+ minimize?: boolean
35
+ }>
36
+
37
+ /** Initial endpoint selection and immutable options for one Process. */
38
+ export type Launch = Readonly<{
39
+ name?: string
40
+ server?: boolean
41
+ client?: boolean | LaunchClient
42
+ options?: Readonly<Record<string, string>>
43
+ }>