@phreshos/core 0.1.0 → 0.1.1

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/LICENSE +19 -0
  2. package/README.md +246 -75
  3. package/dist/askable.d.ts +5 -1
  4. package/dist/channel.d.ts +3 -2
  5. package/dist/client.d.ts +5 -5
  6. package/dist/color.d.ts +10 -0
  7. package/dist/color.js +14 -0
  8. package/dist/config.d.ts +22 -11
  9. package/dist/config.js +0 -6
  10. package/dist/endpoint.d.ts +6 -5
  11. package/dist/launch.d.ts +10 -2
  12. package/dist/main.d.ts +11 -4
  13. package/dist/main.js +8 -1
  14. package/dist/outcome.d.ts +4 -0
  15. package/dist/permissions.d.ts +34 -0
  16. package/dist/permissions.js +6 -0
  17. package/dist/process.d.ts +5 -9
  18. package/dist/program.d.ts +24 -14
  19. package/dist/publishable.d.ts +5 -1
  20. package/dist/scale.d.ts +12 -0
  21. package/dist/scale.js +29 -0
  22. package/dist/server.d.ts +4 -4
  23. package/dist/sql.d.ts +7 -0
  24. package/dist/storage.d.ts +10 -0
  25. package/dist/subscribable.d.ts +32 -3
  26. package/dist/theme.d.ts +132 -0
  27. package/dist/theme.js +34 -0
  28. package/dist/timeout.d.ts +5 -0
  29. package/dist/timeout.js +0 -0
  30. package/dist/value.d.ts +17 -0
  31. package/dist/value.js +118 -0
  32. package/dist/wallpaper.d.ts +30 -0
  33. package/dist/wallpaper.js +0 -0
  34. package/dist/window.d.ts +50 -12
  35. package/dist/window.js +0 -4
  36. package/package.json +35 -9
  37. package/source/askable.ts +0 -26
  38. package/source/channel.ts +0 -28
  39. package/source/client.ts +0 -29
  40. package/source/config.ts +0 -104
  41. package/source/endpoint.ts +0 -95
  42. package/source/launch.ts +0 -43
  43. package/source/main.ts +0 -65
  44. package/source/outcome.ts +0 -4
  45. package/source/process.ts +0 -77
  46. package/source/program.ts +0 -130
  47. package/source/publishable.ts +0 -33
  48. package/source/served-file.ts +0 -14
  49. package/source/server.ts +0 -58
  50. package/source/sql.ts +0 -36
  51. package/source/storage.ts +0 -66
  52. package/source/subscribable.ts +0 -94
  53. package/source/window.ts +0 -82
package/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2026 Zohayr SLILEH
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19
+ OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,42 +1,131 @@
1
1
  # `@phreshos/core`
2
2
 
3
- The Core SDK defines the domain objects and types shared by the client and
4
- server SDKs. It contains no transport or subscription implementation.
3
+ Environment-neutral contracts and domain objects for PhreshOS Programs. Core
4
+ is the common language shared by the Client, Server, React, CLI, and system
5
+ repositories; it contains no transport, persistence, or interface
6
+ implementation of its own.
7
+
8
+ ## Installation
9
+
10
+ ```sh
11
+ bun add @phreshos/core
12
+ ```
13
+
14
+ Import exclusively through the package's public root:
15
+
16
+ ```ts
17
+ import { defineConfig, type Program } from "@phreshos/core"
18
+ ```
19
+
20
+ The package exposes built JavaScript and declaration files as its only public
21
+ code. Its TypeScript source is repository material, not a runtime dependency
22
+ or a public import path.
5
23
 
6
24
  ## Package status
7
25
 
8
- This package is one component of a larger architecture that is still under
9
- active testing. The architecture's components will be released in stages as
10
- their contracts and integrations are verified.
26
+ This package is one component of a larger architecture currently under active
27
+ testing. The `0.x` line may change as its contracts and integrations are
28
+ verified.
11
29
 
12
30
  `@phreshos/core` is not intended to be used on its own. It defines the shared
13
- contracts consumed by the environment SDKs and does not provide a runtime
31
+ contracts consumed by the environment SDKs, but does not provide a runtime
14
32
  implementation by itself.
15
33
 
16
- `Subscribable` is the independent receiving capability. It provides the
34
+ ## Mental model
35
+
36
+ Programs use Core directly for `defineConfig()` and shared types. At runtime,
37
+ the Client and Server SDKs provide the real `Program`, `Process`, `Server`, and
38
+ `Client` handles backed by their environment boundaries. Core gives those
39
+ environments one vocabulary and stable runtime identity without implementing
40
+ their transport, storage, authority, or presentation.
41
+
42
+ ## Permission names
43
+
44
+ Core owns the finite, system-wide permission registry. `permissionNames` is
45
+ its runtime value, `PermissionName` is the corresponding SDK type, and
46
+ `isPermissionName()` validates values arriving from untyped boundaries. Client
47
+ and Server SDK permission methods accept only this type, so Programs cannot
48
+ create private permission strings that the system does not understand.
49
+
50
+ The current registry contains `pointer`. Persistent snapshots use
51
+ `PermissionDecisions`, a partial map, since an absent permission simply has no
52
+ stored decision.
53
+
54
+ ## Theme lifecycle
55
+
56
+ Core defines both `ThemeProperties` and the read-only `Theme` contract without
57
+ any knowledge of how a given interface renders them. `snapshot()` is an
58
+ explicit, asynchronous read of one complete, immutable value. `Theme` extends
59
+ the ordinary `Subscribable` capability with a `change` event; it has no
60
+ dedicated subscription shape of its own.
61
+
62
+ Theme properties are expressed as concrete default values. `themeLimits`
63
+ declares the system-owned customization bounds for spacing, corner radius, and
64
+ glass-material properties; implementing authorities validate replacements
65
+ against those bounds. Core defines one fixed numeric scale around any explicit
66
+ number and one fixed color scale around any explicit CSS color — it does not
67
+ prescribe which value an interface ultimately chooses to derive. Background,
68
+ foreground, and accent are independent CSS color sources, with standard values
69
+ of `#edf8fc`, `#183447`, and `#4c9cff` — the established pale surface, primary
70
+ ink, and original system blue, respectively. Core stores only those source
71
+ colors, never their derived treatments. The declared glass-opacity range ends
72
+ at `0.3`.
73
+
74
+ `standardTheme` is the single, complete initial value shared by every
75
+ environment; the running system remains the authority for its current value.
76
+ `createThemeSnapshot()` copies and freezes a complete replacement at the
77
+ contract boundary; validation remains the implementing authority's
78
+ responsibility.
79
+
80
+ Derived variants are calculations, not persisted `Theme` state. `numericScale()`
81
+ produces `xsmall`, `small`, `medium`, `large`, and `xlarge`, preserving the
82
+ supplied value exactly at `medium`. `color()` produces `subtle`, `soft`, `base`,
83
+ `strong`, and `intense`, preserving the supplied color exactly at `base`.
84
+
85
+ `subscribe("change", listener)` delivers only complete replacements published
86
+ after that registration exists. It never supplies an initial snapshot and
87
+ never replays a change published earlier; Programs request the current
88
+ snapshot explicitly when they need one. `WritableTheme` adds asynchronous
89
+ authority to replace the value — a read-only environment exposes `Theme`,
90
+ while an authorized environment may expose `WritableTheme` without altering
91
+ the shared lifecycle.
92
+
93
+ `Colorable`, `Sizable`, `Shapeable`, `Variantable`, and `Elevatable` are
94
+ independent element capabilities whose concrete value vocabularies are defined
95
+ separately. An element composes only the capabilities it actually supports.
96
+
97
+ These contracts contain no React types, component names, or runtime `Theme`
98
+ authority. Environment and interface SDKs build those concerns on top of the
99
+ same neutral lifecycle and derivations.
100
+
101
+ ## Messaging primitives
102
+
103
+ `Subscribable` is the independent receiving capability, providing the
17
104
  contracts for `subscribe()`, `waitFor()`, `events()`, and `observe()`.
18
105
  `Publishable` independently provides `publish()`. `Askable` extends
19
- `Publishable` with `ask()`, because every target that can be asked can also be
20
- published to. A type may implement or compose only the capabilities it needs;
21
- being able to publish does not imply being able to subscribe.
106
+ `Publishable` with `ask()`, since every target that can be asked can also be
107
+ published to. Both operations accept a single payload, which is `undefined`
108
+ when omitted. A type may implement or compose only the capabilities it needs —
109
+ the ability to publish does not imply the ability to subscribe.
22
110
 
23
- Finite asynchronous operations use a ten-second SDK deadline by default.
24
- `Askable.timeout()` creates an immutable deadline view for `ask()` only;
25
- `waitFor()` and `waitReady()` accept a deadline directly. `events()` is a
26
- long-lived iterator with a bounded queue rather than a timed request. An ask's
27
- single deadline covers both waiting for its current Server incarnation to
28
- become ready and waiting for the eventual answer.
111
+ The contracts specify a ten-second default SDK deadline for finite asynchronous
112
+ operations. `Askable.timeout()` creates an immutable deadline view scoped to
113
+ `ask()`; `waitFor()` and `waitReady()` accept a deadline directly. `events()`
114
+ is specified as a long-lived iterator with a queue capacity of `64` by default,
115
+ while `Infinity` removes that bound. A single ask deadline covers both waiting
116
+ for its current Server incarnation to become ready and waiting for the eventual
117
+ answer. Environment SDKs implement these guarantees.
29
118
 
30
- Every persistent registration returns its only cleanup function:
119
+ Every persistent registration returns its own cleanup function:
31
120
 
32
121
  ```ts
33
122
  const stop = target.subscribe("event", handler)
34
123
  stop()
35
124
  ```
36
125
 
37
- There are no separate `unsubscribe()` or `unobserve()` methods and no
126
+ There are no separate `unsubscribe()` or `unobserve()` methods, and no
38
127
  one-time subscription operation. Observation APIs such as `observeAsks()` and
39
- `observeAnswers()` follow the same returned-cleanup rule.
128
+ `observeAnswers()` follow the same returned-cleanup convention.
40
129
 
41
130
  ```ts
42
131
  import type { Subscribable } from "@phreshos/core"
@@ -57,78 +146,143 @@ target.subscribe("exit", message => {
57
146
  ```
58
147
 
59
148
  Known messages are inferred from the target. An explicit generic or callback
60
- annotation may narrow that message, but an incompatible replacement is a type
61
- error. `observe()` receives a `Capture` containing only the facts guaranteed by
62
- every Subscribable: `event` and `message`. It makes no assumption about a
63
- sender or destination.
64
-
65
- An unparameterized Channel or traffic surface accepts application-defined event
66
- names with an `unknown` payload. Supplying an event map narrows both the names
67
- and their payloads; names outside that map are then rejected.
68
-
69
- `events()` applies the same message inference and narrowing rules and exposes a
70
- named event as an `AsyncIterableIterator`.
71
-
72
- `Endpoint` is the shared base of Server and Client and composes only
73
- `Publishable`. It is an address and lifecycle-control handle, never a receiving
74
- surface. Server additionally composes `Askable`. Every event explicitly
75
- addressed to the executing Endpoint enters only through its contextual
76
- `Channel`, whose `ChannelMessage` contains the otherwise unknown sender. An
77
- environment may narrow that reference to `Endpoint | null` when its isolation
78
- boundary deliberately hides foreign identities.
79
-
80
- Broad inspection is deliberately separated as `endpoint.traffic`. The Endpoint
81
- handle already identifies the source, so ordinary traffic messages contain only
82
- the unknown destination and payload. Every traffic surface exposes
83
- `observeAsks()` because either Endpoint kind may originate a question. A
84
- Server's traffic additionally exposes `observeAnswers()`, because only a Server
149
+ annotation may narrow that message further, but an incompatible replacement is
150
+ a type error. `observe()` receives a `Capture` containing only the facts
151
+ guaranteed by every `Subscribable`: `event` and `message`. It makes no
152
+ assumption about a sender or destination.
153
+
154
+ An unparameterized Endpoint, Channel, or traffic surface accepts
155
+ application-defined event names with an `unknown` payload. Supplying an event
156
+ map narrows both the names and their payloads, and names outside that map are
157
+ then rejected. `events()` applies the same message inference and narrowing
158
+ rules, exposing a named event as an `AsyncIterableIterator`.
159
+
160
+ ## Endpoints, Channels, and traffic
161
+
162
+ `Endpoint` is the shared base of `Server` and `Client`. It functions as both
163
+ an address and a source: `endpoint.publish()` sends directly to it, while
164
+ `endpoint.subscribe()` and `endpoint.observe()` follow destinationless events
165
+ emitted by it. `Server` additionally composes `Askable`.
166
+
167
+ The contextual `Channel` is the executing Endpoint's inward boundary.
168
+ Subscriptions receive events explicitly addressed to it, delivered as a
169
+ `ChannelMessage` containing the otherwise unknown sender. `channel.publish()`
170
+ emits outward from that executing Endpoint without naming a destination.
171
+
172
+ Directed inspection remains deliberately separate, exposed as
173
+ `endpoint.traffic`. Because the Endpoint handle already identifies the source,
174
+ ordinary traffic messages contain only the destination and payload; Channel
175
+ emissions never enter this surface. Every traffic surface exposes
176
+ `observeAsks()`, since either Endpoint kind may originate a question. A
177
+ Server's traffic additionally exposes `observeAnswers()`, since only a Server
85
178
  can originate an answer. These captures include their event, correlation ID,
86
179
  destination, and question payload or answer `Outcome`.
87
180
 
88
181
  Every Endpoint exposes `exists()`, `start()`, and `stop()`. Endpoint lifecycle
89
- is observed through the owning Process's `serverStart`, `serverStop`,
90
- `clientStart`, and `clientStop` events rather than through Endpoint
91
- subscriptions.
182
+ is observed through `endpointStart` and `endpointStop` at Process, Program,
183
+ and Server Host scope. A start delivers the permanent `Server | Client`
184
+ handle; a stop delivers that same permanent handle directly. Every scope
185
+ receives the same canonical Endpoint instance, allowing comparison by
186
+ identity. Neither lifecycle event wraps the Endpoint or carries process-exit
187
+ details, and Endpoint subscriptions themselves carry only application events
188
+ emitted by that Endpoint.
92
189
 
93
190
  `Server` and `Client` are public, logic-free Endpoint specializations. Server
94
- adds one-payload `ask()` and answer traffic observation. Client returns its owned
95
- Window. Window returns its owning Client, preserving navigation in both
96
- directions without introducing host or transport knowledge.
97
-
98
- Program and Process complete the ownership hierarchy. Every public domain
99
- object remains a real runtime class for identity and `instanceof`, while the
100
- capabilities it implements are independent interfaces. Constructors are public
101
- and logic-free during this contract phase; all methods remain declarations
102
- until an environment SDK supplies their implementation.
103
- Core also owns the common launch, geometry, lifecycle-message, ChannelMessage,
104
- ChannelCapture, TrafficMessage, and TrafficCapture types used by both
191
+ adds request-response `ask()` and answer-traffic observation. Client owns two
192
+ permanent, synchronous capability objects: `traffic` for communication and
193
+ `window` for presentation. Accessing either object performs no operation and
194
+ receives no data — their methods and subscriptions are the explicit
195
+ operations.
196
+
197
+ ## Ownership hierarchy
198
+
199
+ `Program` and `Process` complete the ownership hierarchy. `Program`,
200
+ `Process`, `Endpoint`, `Server`, and `Client` are real runtime classes,
201
+ usable for identity and `instanceof` checks. Their capability properties are
202
+ interfaces: `client.traffic` and `client.window`, in particular, have no
203
+ runtime class identity of their own. Core domain constructors are protected —
204
+ environment SDKs supply the authoritative handles backed by their respective
205
+ boundaries while reusing these exact Core constructors.
206
+
207
+ Core also owns the common launch, geometry, lifecycle-message, `ChannelMessage`,
208
+ `ChannelCapture`, `TrafficMessage`, and `TrafficCapture` types used by both
105
209
  environments.
106
210
 
107
- Process parentage belongs to `Process`, not to contextual SDK state.
108
- Each live Process retains only a handle to its immediate parent.
109
- `process.parent()` returns `null` when no accessible parent handle exists. It
110
- does not preserve historical lineage: calling it through an exited Process
111
- handle, or after the retained parent has disappeared, rejects because the
112
- represented Process does not exist. A future `current.parent()` may flatten
113
- this operation, but does not own the relationship.
211
+ ## Window placement
212
+
213
+ Window placement is expressed through two deliberately separate contracts.
214
+ Authoring `Layer` contains only `under`, `window`, and `over`, so that
215
+ Program declarations and Client launch overrides cannot request system-owned
216
+ placement. Runtime `WindowLayer` adds `wallpaper`, allowing `window.layer()`
217
+ to report the truth for a wallpaper Window without making that layer
218
+ constructible from Program code. `Window` has no identity or lifecycle apart
219
+ from its Client; its stable capability addresses the Client's current, live
220
+ presentation state and rejects reads or mutations while that Client is
221
+ absent.
222
+
223
+ Core also defines the independent, environment-neutral `FileWallpaper` and
224
+ `DesktopWallpaper` contracts. The desktop variant additionally accepts a
225
+ Program and the deliberately narrow `WallpaperLaunch`, containing only `name`,
226
+ `server`, the Client's initial `location`, and immutable Process `options`.
227
+ These contracts describe capability and data shape only — they contain no
228
+ persistence, upload, Process creation, or rendering implementation.
229
+
230
+ ## Window Surface
231
+
232
+ `client.window.surface` addresses the optional host-rendered material belonging
233
+ to one Window. Its source of truth is authoritative, server-owned Window state,
234
+ not iframe state. `snapshot()` explicitly reads the current target, while the
235
+ ordinary `change` subscription receives only replacements published after that
236
+ subscription exists. A `null` target means no Surface exists in the render
237
+ tree.
238
+
239
+ Only Windows currently occupying the `under` or `over` layer may call `set()`
240
+ or `remove()`; `window` and `wallpaper` layers reject those operations. Calling
241
+ `set()` without settings creates a sharp, fully opaque Surface. Settings may
242
+ select opacity from zero to one, a nonnegative pixel radius, a scale level, or
243
+ `"full"`, and an optional transaction containing a duration from zero through
244
+ 60,000 milliseconds and a stable named or cubic Bézier easing. Zero opacity
245
+ retains the Surface node; only `remove()` returns the authoritative target
246
+ immediately to `null`. The server validates settings, and the desktop animates
247
+ from the rendered values to each new target.
248
+
249
+ ## Window geometry
250
+
251
+ Window geometry follows one public grammar. A `Value` is either a finite
252
+ pixel number or a linear relative expression such as `"1/2"`, `"50% + 10"`, or
253
+ `"30% + 20 * 2"`. `isRelativeValue()` validates that grammar, and
254
+ `parseRelativeValue()` reduces it to a single relative coefficient and pixel
255
+ offset. The CLI, runtime validation, and desktop layout all consume this one
256
+ Core definition rather than maintaining separate parsers.
257
+
258
+ ## Process parentage
259
+
260
+ Process parentage belongs to `Process`, not to contextual SDK state. Each
261
+ live Process retains only a handle to its immediate parent. `process.parent()`
262
+ returns `null` when no accessible parent handle exists, and does not preserve
263
+ historical lineage: calling it through an exited Process handle, or after the
264
+ retained parent has disappeared, rejects outright, since the represented
265
+ Process no longer exists. `current.parent()` flattens this operation
266
+ contextually but does not itself own the relationship.
114
267
 
115
268
  Client traversal is structurally confined to its current Program. A
116
- cross-Program parent handle is never supplied to the client, so traversal stops
117
- at `null`. A fabricated or otherwise unauthorized Process handle is
118
- indistinguishable from a nonexistent Process and every operation through it
119
- rejects accordingly.
269
+ cross-Program parent handle is never supplied to the Client, so traversal
270
+ stops at `null`. A fabricated or otherwise unauthorized Process handle is
271
+ indistinguishable from a nonexistent Process, and every operation performed
272
+ through it rejects accordingly.
120
273
 
121
274
  ## Program configuration
122
275
 
123
- Core is also the single source of truth for the authoring contract consumed by
124
- the CLI. `defineConfig()` provides contextual typing for `phresh.config.ts` and
125
- returns the description unchanged:
276
+ Core is also the single source of truth for the authoring contract consumed
277
+ by the CLI. `defineConfig()` provides contextual typing for
278
+ `phresh.config.ts` and returns the description unchanged:
126
279
 
127
280
  ```ts
128
281
  import { defineConfig } from "@phreshos/core"
129
282
 
130
283
  export default defineConfig({
131
284
  identity: "my-program",
285
+ icon: "./icon.png",
132
286
  server: {
133
287
  location: "./dist/server",
134
288
  startCommand: "node main.js",
@@ -146,9 +300,22 @@ export default defineConfig({
146
300
  })
147
301
  ```
148
302
 
149
- The declaration must contain a Server, a Client, or both. Development settings
150
- remain authoring metadata; the CLI derives the appropriate runtime description
151
- for each mode.
303
+ The declaration must include a Server, a Client, or both. Development
304
+ settings remain authoring metadata; the CLI derives the appropriate runtime
305
+ description for each mode. The optional `icon` field names a single PNG
306
+ source — packaging and installation normalize it to `icon.png`, while hosting
307
+ derives the system's fixed presentation sizes.
308
+
309
+ Every Program also exposes its guaranteed icon without revealing hosting or
310
+ filesystem details:
311
+
312
+ ```ts
313
+ const icon = await program.icon() // medium PNG Blob
314
+ const large = await program.icon("large") // small, medium, or large
315
+ ```
316
+
317
+ This method always returns an `image/png` `Blob`. When no icon has been
318
+ authored, the system returns its default through the same operation.
152
319
 
153
320
  ## Program-owned resources
154
321
 
@@ -157,3 +324,7 @@ filesystem-like `data` and `cache`, a key-value `store`, read-only SQL `logs`,
157
324
  and a writable SQLite `database`. The Server SDK refines its filesystem areas
158
325
  with `path()` and safe `resolve()` access; these host filesystem paths are
159
326
  structurally absent from the Client SDK.
327
+
328
+ ## License
329
+
330
+ Licensed under the [MIT License](LICENSE). Copyright © 2026 Zohayr SLILEH.
package/dist/askable.d.ts CHANGED
@@ -1,11 +1,14 @@
1
1
  import type { Publishable } from "./publishable.js";
2
+ import type { Timeoutable } from "./timeout.js";
2
3
  /** An immutable Askable view using one caller-selected deadline. */
3
4
  export interface TimedAskable {
5
+ /** Sends a question with an `undefined` payload and waits within the selected deadline. */
6
+ ask<Answer = unknown>(event: string): Promise<Answer>;
4
7
  /** Sends one question and waits within the selected deadline. */
5
8
  ask<Answer = unknown, Payload = unknown>(event: string, payload: Payload): Promise<Answer>;
6
9
  }
7
10
  /** A publishing target that can also receive a question and return an answer. */
8
- export interface Askable<Events extends object = {}, Fallback = unknown> extends Publishable<Events, Fallback> {
11
+ export interface Askable<Events extends object = {}, Fallback = unknown> extends Publishable<Events, Fallback>, Timeoutable<TimedAskable> {
9
12
  /**
10
13
  * Sends one question payload to this target and waits for its answer.
11
14
  *
@@ -16,6 +19,7 @@ export interface Askable<Events extends object = {}, Fallback = unknown> extends
16
19
  * readiness and the answer. The boundary cannot infer whether an answerer
17
20
  * exists, so a ready unanswered question waits for that deadline.
18
21
  */
22
+ ask<Answer = unknown>(event: string): Promise<Answer>;
19
23
  ask<Answer = unknown, Payload = unknown>(event: string, payload: Payload): Promise<Answer>;
20
24
  /** Returns an immutable view whose `ask()` uses this deadline in milliseconds. */
21
25
  timeout(milliseconds: number): TimedAskable;
package/dist/channel.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Endpoint } from "./endpoint.js";
2
+ import type { Publishable } from "./publishable.js";
2
3
  import type { Captures, Subscribable } from "./subscribable.js";
3
4
  /** One application value arriving through the current Endpoint's Channel. */
4
5
  export type ChannelMessage<Payload = unknown, From = Endpoint> = Readonly<{
@@ -14,7 +15,7 @@ export type ChannelEvents<Events extends object, From = Endpoint> = {
14
15
  type ChannelFallback<Events extends object, From> = keyof Events extends never ? ChannelMessage<unknown, From> : never;
15
16
  /** Every application event observable through a Channel. */
16
17
  export type ChannelCapture<Events extends object = {}, From = Endpoint> = Captures<ChannelEvents<Events, From>, ChannelFallback<Events, From>>;
17
- /** Events explicitly accepted by the current Endpoint. */
18
- export interface Channel<Events extends object = {}, From = Endpoint> extends Subscribable<ChannelEvents<Events, From>, ChannelFallback<Events, From>> {
18
+ /** Addressed input and destinationless output for the executing Endpoint. */
19
+ export interface Channel<Events extends object = {}, From = Endpoint> extends Subscribable<ChannelEvents<Events, From>, ChannelFallback<Events, From>>, Publishable {
19
20
  }
20
21
  export {};
package/dist/client.d.ts CHANGED
@@ -2,18 +2,18 @@ import { Endpoint, type EndpointTraffic } from "./endpoint.js";
2
2
  import type { LaunchClient } from "./launch.js";
3
3
  import type { Server } from "./server.js";
4
4
  import type { Window } from "./window.js";
5
- /** Broad communication originating from one Client. */
5
+ /** Directed communication originating from one Client. */
6
6
  export interface ClientTraffic<Events extends object = {}, To = Endpoint, AskTo = Server> extends EndpointTraffic<Events, To, AskTo> {
7
7
  }
8
8
  /** The client Endpoint of a Process. */
9
9
  export declare class Client<Events extends object = {}> extends Endpoint<Events> {
10
- constructor();
10
+ protected constructor();
11
11
  }
12
12
  export interface Client<Events extends object = {}> {
13
- /** Broad communication originating from this Client. */
13
+ /** Directed communication originating from this Client. */
14
14
  readonly traffic: ClientTraffic<Events>;
15
+ /** Presentation capability permanently owned by this Client handle. */
16
+ readonly window: Window;
15
17
  /** Starts a fresh Client and Window using optional Process-local overrides. */
16
18
  start(overrides?: LaunchClient): Promise<void>;
17
- /** Returns the Window owned by this live Client. */
18
- window(): Promise<Window>;
19
19
  }
@@ -0,0 +1,10 @@
1
+ /** Semantic treatments derived from one explicit CSS color. */
2
+ export type ColorLevel = "subtle" | "soft" | "base" | "strong" | "intense";
3
+ /** Complete color scale derived from one explicit CSS color. */
4
+ export type ColorScale = Readonly<Record<ColorLevel, string>>;
5
+ /**
6
+ * Derives a balanced color scale while preserving the supplied color as its
7
+ * exact base. CSS performs the mixing in OKLCH, so every valid CSS color can be
8
+ * the source without the system maintaining a second palette.
9
+ */
10
+ export declare function color(value: string): ColorScale;
package/dist/color.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Derives a balanced color scale while preserving the supplied color as its
3
+ * exact base. CSS performs the mixing in OKLCH, so every valid CSS color can be
4
+ * the source without the system maintaining a second palette.
5
+ */
6
+ export function color(value) {
7
+ return Object.freeze({
8
+ subtle: `color-mix(in oklch, ${value} 25%, white)`,
9
+ soft: `color-mix(in oklch, ${value} 60%, white)`,
10
+ base: value,
11
+ strong: `color-mix(in oklch, ${value} 82%, black)`,
12
+ intense: `color-mix(in oklch, ${value} 68%, black)`
13
+ });
14
+ }
package/dist/config.d.ts CHANGED
@@ -52,30 +52,41 @@ type Description = Readonly<{
52
52
  version?: string;
53
53
  /** Short human-readable explanation of what the Program does. */
54
54
  description?: string;
55
- /** Markdown file that officially introduces the Program's API. */
55
+ /**
56
+ * Markdown file describing only the Program-owned API contract.
57
+ * System access, Endpoint, and event mechanics belong to system documentation.
58
+ */
56
59
  apiDocs?: string;
57
- /** Directory containing the Program's sized icons. */
58
- icons?: string;
60
+ /** PNG source between 128 and 2,048 pixels per side and no larger than 5 MiB. */
61
+ icon?: string;
59
62
  /** Command run before production start, installation, and packaging. */
60
63
  buildCommand?: string;
61
64
  }>;
65
+ type ServerProgramConfig = Description & Readonly<{
66
+ /** Server declaration for this Program. */
67
+ server: ServerConfig;
68
+ /** Optional Client declaration for this Program. */
69
+ client?: ClientConfig;
70
+ }>;
71
+ type ClientProgramConfig = Description & Readonly<{
72
+ /** Optional Server declaration for this Program. */
73
+ server?: ServerConfig;
74
+ /** Client declaration for this Program. */
75
+ client: ClientConfig;
76
+ }>;
62
77
  /**
63
78
  * The authoring description read from `phresh.config.ts`.
64
79
  *
65
80
  * A Program must declare a Server, a Client, or both.
66
81
  */
67
- export type Config = Description & (Readonly<{
68
- server: ServerConfig;
69
- client?: ClientConfig;
70
- }> | Readonly<{
71
- server?: ServerConfig;
72
- client: ClientConfig;
73
- }>);
82
+ export type Config = ServerProgramConfig | ClientProgramConfig;
74
83
  /**
75
84
  * Defines a Program authoring description with contextual typing.
76
85
  *
77
86
  * This helper performs no work and returns the supplied description unchanged.
78
87
  * The CLI validates and derives it for development, production, or packaging.
79
88
  */
80
- export declare function defineConfig<const Description extends Config>(config: Description): Description;
89
+ export declare function defineConfig(config: ServerProgramConfig): Config;
90
+ /** Defines a Program authoring description with contextual typing. */
91
+ export declare function defineConfig(config: ClientProgramConfig): Config;
81
92
  export {};
package/dist/config.js CHANGED
@@ -1,9 +1,3 @@
1
- /**
2
- * Defines a Program authoring description with contextual typing.
3
- *
4
- * This helper performs no work and returns the supplied description unchanged.
5
- * The CLI validates and derives it for development, production, or packaging.
6
- */
7
1
  export function defineConfig(config) {
8
2
  return config;
9
3
  }
@@ -34,20 +34,21 @@ export type AskCapture<Payload = unknown, To = Server> = Readonly<{
34
34
  export type AskObserver<Payload = unknown, To = Server> = (capture: AskCapture<Payload, To>) => unknown;
35
35
  /** Every ordinary publication observable in traffic from one Endpoint. */
36
36
  export type TrafficCapture<Events extends object = {}, To = Endpoint> = Captures<TrafficEvents<Events, To>, TrafficFallback<Events, To>>;
37
- /** Broad communication originating from one Endpoint, regardless of destination. */
37
+ /** Directed communication originating from one Endpoint. */
38
38
  export interface EndpointTraffic<Events extends object = {}, To = Endpoint, AskTo = Server> extends Subscribable<TrafficEvents<Events, To>, TrafficFallback<Events, To>> {
39
39
  /** Observes questions originating from this Endpoint. */
40
40
  observeAsks<Payload = unknown>(observer: AskObserver<Payload, AskTo>): Cleanup;
41
41
  }
42
42
  /** The shared Process endpoint represented by Server and Client. */
43
43
  export declare class Endpoint<Events extends object = {}> {
44
- constructor();
44
+ protected constructor();
45
45
  }
46
- export interface Endpoint<Events extends object = {}> extends Publishable {
47
- /** Broad communication originating from this Endpoint. */
46
+ /** An Endpoint address that can also be followed as a destinationless source. */
47
+ export interface Endpoint<Events extends object = {}> extends Publishable, Subscribable<Events, keyof Events extends never ? unknown : never> {
48
+ /** Directed communication originating from this Endpoint. */
48
49
  readonly traffic: EndpointTraffic<Events>;
49
50
  /** Returns the Process that owns this Endpoint. */
50
- process(): Process;
51
+ process(): Promise<Process>;
51
52
  /** Returns whether this Endpoint currently has a live incarnation. */
52
53
  exists(): Promise<boolean>;
53
54
  /**
package/dist/launch.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- /** A pixel count or a relative linear expression. */
2
- export type Value = number | string;
1
+ import type { Value } from "./value.js";
3
2
  /** A Window's top-left position. */
4
3
  export type Position = Readonly<{
5
4
  /** Horizontal position. */
@@ -20,16 +19,25 @@ export type Layer = "window" | "under" | "over";
20
19
  export declare const layers: readonly Layer[];
21
20
  /** Per-Process overrides used when starting a Client and its Window. */
22
21
  export type LaunchClient = Readonly<{
22
+ /** Initial Window size for this Process. */
23
23
  size?: Size;
24
+ /** Initial Window position for this Process. */
24
25
  position?: Position;
26
+ /** Structurally isolated layer containing this Process's Window. */
25
27
  layer?: Layer;
28
+ /** Initial page beneath the Client's declared location scope. */
26
29
  location?: string;
30
+ /** Whether the Window initially opens minimized. */
27
31
  minimize?: boolean;
28
32
  }>;
29
33
  /** Initial endpoint selection and immutable options for one Process. */
30
34
  export type Launch = Readonly<{
35
+ /** Optional meaningful name unique among this Program's live Processes. */
31
36
  name?: string;
37
+ /** Whether to start the declared Server. Uses its declaration when omitted. */
32
38
  server?: boolean;
39
+ /** Whether and how to start the declared Client. Uses its declaration when omitted. */
33
40
  client?: boolean | LaunchClient;
41
+ /** Immutable string options readable by the created Process. */
34
42
  options?: Readonly<Record<string, string>>;
35
43
  }>;