gesso-framework 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.
@@ -0,0 +1,716 @@
1
+ import { BehaviorSubject, Observable, Subject, Subscription } from "rxjs";
2
+ import { LayoutBox, Reactive, UiChild, UiModifier } from "gesso-core";
3
+ //#region src/Component.d.ts
4
+ /**
5
+ * Base class for all Gesso framework components.
6
+ *
7
+ * Components are class-based. They declare reactive state with
8
+ * an `internalState()` cell, inputs with `@Input()`, and return a UiElement tree
9
+ * from `render()`. The framework calls `render()` once per mount;
10
+ * after that, observable emissions in the returned tree drive updates.
11
+ */
12
+ declare abstract class Component {
13
+ /**
14
+ * Called once after the component's UiNode subtree has been created
15
+ * and all observable bindings are connected.
16
+ */
17
+ onMount?(): void;
18
+ /**
19
+ * Called once when the component is being removed from the graph.
20
+ */
21
+ onUnmount?(): void;
22
+ /**
23
+ * Returns the component's UI definition.
24
+ *
25
+ * Called exactly once per component instance. Dynamic content is
26
+ * expressed through Observable props and children, never by
27
+ * re-invoking render().
28
+ *
29
+ * Returning an Observable is allowed for structural changes that
30
+ * cannot be expressed as observable children (routing, for example).
31
+ * The framework reconciles the component's root on each emission.
32
+ */
33
+ abstract render(): UiChild;
34
+ }
35
+ //#endregion
36
+ //#region src/InternalState.d.ts
37
+ /**
38
+ * A component's own state: originated here, and never crossing the
39
+ * barrier.
40
+ *
41
+ * The writable counterpart to `InputCell`. The two are the same
42
+ * `BehaviorSubject` and differ by one accessor — this one has a
43
+ * `.value` setter — and that difference is the whole semantics:
44
+ *
45
+ * internalState() never crosses I write it
46
+ * input() crosses inward someone else writes it
47
+ *
48
+ * Named on that axis deliberately. It used to be `state()`, which
49
+ * described *what* a thing was while `input()` described *where it
50
+ * came from*; two names on two axes made neither of them tell you
51
+ * anything about the other. `internalState(products)` reads as a
52
+ * mistake at the call site in a way `state(products)` never did.
53
+ *
54
+ * For values that originate on this thread and die with the component:
55
+ * a tooltip's open flag, a caret, a scroll offset, the active tab.
56
+ * Anything that survives a reload, or that another screen cares about,
57
+ * is application state and belongs on a channel. Anything derived from
58
+ * other cells is a `computed`.
59
+ *
60
+ * It is not only for components. The thread that owns a channel's data
61
+ * writes cells too, and wrote them as a `BehaviorSubject` mirrored
62
+ * into an `asObservable()` because this was reachable only through the
63
+ * framework's main entry. `gesso-framework/worker` is the same cell
64
+ * with none of the renderer behind it, so an application worker holds
65
+ * one cell rather than a subject and a copy of it, and reads it with
66
+ * `.value` in a `computed` rather than listing it in a
67
+ * `combineLatest`. "Internal" still means what it says there: written
68
+ * here, and crossing the barrier only as the plain data a view key
69
+ * publishes.
70
+ */
71
+ declare class InternalState<T> extends BehaviorSubject<T> {
72
+ /** What to call this cell in a warning or the inspector; optional. */
73
+ label: string | undefined;
74
+ constructor(initialValue: T);
75
+ get value(): T;
76
+ set value(next: T);
77
+ }
78
+ /**
79
+ * Creates a reactive state cell.
80
+ *
81
+ * Usage inside a component:
82
+ *
83
+ * private readonly count = state(0);
84
+ *
85
+ * increment() {
86
+ * this.count.value++;
87
+ * }
88
+ */
89
+ declare function internalState<T>(initialValue: T, label?: string): InternalState<T>;
90
+ //#endregion
91
+ //#region src/Input.d.ts
92
+ /**
93
+ * Reactive cell holding a component input.
94
+ *
95
+ * Inputs must be cells because render() runs exactly once. A component
96
+ * that read a plain input value during render would capture it for the
97
+ * life of the instance, so a parent supplying new props under a
98
+ * dynamic subtree would update the field while the rendered tree kept
99
+ * showing the original value.
100
+ *
101
+ * The cell is written by the component host only: it accepts whatever
102
+ * the parent passed, subscribing it first when the parent passed an
103
+ * Observable. `value` is deliberately read-only, so `this.label.value =
104
+ * x` inside a component is a compile error rather than a silent
105
+ * violation of the one-way data flow.
106
+ */
107
+ declare class InputCell<T> extends BehaviorSubject<T> {
108
+ /**
109
+ * What to call this cell in a warning: `Component.prop`, or
110
+ * `channel.key`. Set by whoever creates it; a cell without one is
111
+ * reported as "a cell".
112
+ */
113
+ label: string | undefined;
114
+ /** The component whose body read `.value`, for the stale-read warning below. */
115
+ private snapshotBy;
116
+ private warnedStale;
117
+ /** Everything emitted through `emit`, created on the first `events` read. */
118
+ private emitted;
119
+ constructor(initialValue: T);
120
+ /**
121
+ * Fires the output this cell stands for.
122
+ *
123
+ * An output is a cell whose value is what the parent gave to be
124
+ * called: a function, or a target made by `into(subject)`. `emit`
125
+ * calls it with the arguments, and also pushes the first argument
126
+ * through `events`, so the component can fire from three places
127
+ * without passing the cell around and a parent that wants a stream
128
+ * can have one. A parent that passed nothing is fine: the call
129
+ * simply reaches nobody.
130
+ */
131
+ emit(...args: EmitArgs<T>): void;
132
+ /** What `emit` has fired, as a stream: the first argument of each call. */
133
+ get events(): Observable<EmitValue<T>>;
134
+ get value(): T;
135
+ /**
136
+ * The one place the run-once model goes quietly wrong is a body that
137
+ * reads `props.x.value`, uses the value to build the tree, and never
138
+ * hears that it changed. Nothing crashes; the screen is simply stale.
139
+ * So a cell remembers being read while a body ran, and if it later
140
+ * changes with nobody subscribed to it, it says so once. A cell that
141
+ * something is following is fine: the follower carries the change.
142
+ */
143
+ next(value: T): void;
144
+ }
145
+ /**
146
+ * Anything with a current value that can be followed: an input, an
147
+ * internal state, a channel view key, a computed. What `computed`
148
+ * collects as it runs.
149
+ */
150
+ interface ReadableCell<T> extends Observable<T> {
151
+ readonly value: T;
152
+ }
153
+ /**
154
+ * Creates a component input cell.
155
+ *
156
+ * Inside a class component the argument is the default the cell holds
157
+ * until the parent supplies a value:
158
+ *
159
+ * @Input() label = input('Count');
160
+ *
161
+ * Inside a functional component the props are already cells; the
162
+ * two-argument form derives a cell that replaces `undefined` with a
163
+ * fallback, which is how an optional prop gets its default:
164
+ *
165
+ * function Counter(inputs: Inputs<{ label?: string }>) {
166
+ * const label = input(inputs.label, 'Count'); // InputCell<string>
167
+ * return Text({ text: label });
168
+ * }
169
+ *
170
+ * The derived cell follows the source for the life of the component
171
+ * and completes when the source does, so it needs no teardown.
172
+ */
173
+ declare function input<T>(initialValue: T): InputCell<T>;
174
+ declare function input<T>(source: InputCell<T | undefined>, fallback: T): InputCell<T>;
175
+ /** The arguments `emit` takes for a cell holding a handler of type `T`. */
176
+ type EmitArgs<T> = NonNullable<T> extends ((...args: infer A) => void) ? A : never;
177
+ /** What `events` carries for a cell holding a handler of type `T`: the handler's first argument. */
178
+ type EmitValue<T> = NonNullable<T> extends ((first: infer V, ...rest: never[]) => void) ? V : void;
179
+ /**
180
+ * A component's output: an input cell that holds whatever the parent
181
+ * gave to be called, fired through `emit`. Every function-typed member
182
+ * of a component's inputs is one, so on a component declared with
183
+ * `onOpen: (id: string) => void`, `inputs.onOpen.emit(id)` is how the
184
+ * component speaks, and a parent may pass a function or `into(subject)`.
185
+ * The name is for a class field: `@Output() changed = output<[number]>()`.
186
+ */
187
+ type OutputCell<A extends unknown[]> = InputCell<((...args: A) => void) | undefined>;
188
+ /**
189
+ * An output a class component declares as a field:
190
+ *
191
+ * @Output() changed = output<[value: number]>();
192
+ *
193
+ * The host wires the parent's handler into it exactly as it wires an
194
+ * input, and `this.changed.emit(next)` fires it.
195
+ */
196
+ declare function output<A extends unknown[]>(): OutputCell<A>;
197
+ declare const OUTPUT_TARGET: unique symbol;
198
+ /**
199
+ * A parent's way of receiving an output as a stream rather than a call:
200
+ *
201
+ * const opened = new Subject<string>();
202
+ * <Card onOpen={into(opened)} />
203
+ *
204
+ * Wrapped rather than passed bare, because a bare `Subject` is an
205
+ * Observable and would be read as an *input* the parent is feeding the
206
+ * child, which is the opposite direction.
207
+ */
208
+ interface OutputTarget<V> {
209
+ readonly [OUTPUT_TARGET]: {
210
+ next(value: V): void;
211
+ };
212
+ }
213
+ declare function into<V>(target: {
214
+ next(value: V): void;
215
+ }): OutputTarget<V>;
216
+ declare function isOutputTarget(value: unknown): value is OutputTarget<unknown>;
217
+ //#endregion
218
+ //#region src/bounds.d.ts
219
+ /**
220
+ * A node's box, as a cell.
221
+ *
222
+ * `ctx.bounds()` hands one out and the modifier on it fills it, so the
223
+ * arithmetic a pointer position needs starts from a value rather than
224
+ * from `new BehaviorSubject<LayoutBox>({ x: 0, y: 0, width: 0, height: 0 })`
225
+ * and a `measure(subject)` beside it. It is an ordinary cell: read it
226
+ * with `.value` in a handler, bind it, or derive from it.
227
+ *
228
+ * const track = ctx.bounds();
229
+ * <box modifiers={[track.modifier]} onPointerMove={e => at(e.x - track.value.x)} />
230
+ *
231
+ * **It reports a move, and only a move.** `measure` is built on
232
+ * `LayoutNotifier`, which fires for every layout fact a node's listeners
233
+ * could care about, and one of those is a scroll offset that changed
234
+ * while the box stayed exactly where it was. A
235
+ * cell that emitted for those would wake everything derived from it on
236
+ * every frame of every scroll, for a box that did not move, which is
237
+ * the defect this exists to avoid. So an equal box is dropped here,
238
+ * where the comparison is four numbers, rather than by a
239
+ * `distinctUntilChanged` at each of the places that read it.
240
+ *
241
+ * There is no `ctx.bounds(ref)`. A box is reported by a modifier, which
242
+ * is how a node is reached from outside the layout engine everywhere
243
+ * else in this framework, and a ref would be a second way of naming the
244
+ * same node with nothing else built on it.
245
+ */
246
+ declare class BoundsCell extends InternalState<LayoutBox> {
247
+ /** Put this on the element whose box the cell should hold. */
248
+ readonly modifier: UiModifier<Subject<LayoutBox>>;
249
+ constructor();
250
+ /** Writes a box, unless it is the one already held. */
251
+ next(box: LayoutBox): void;
252
+ }
253
+ /**
254
+ * A bounds cell outside a component, for a class component or a test.
255
+ * Inside a function component `ctx.bounds()` is the same thing, and it
256
+ * completes with the component.
257
+ */
258
+ declare function bounds(label?: string): BoundsCell;
259
+ //#endregion
260
+ //#region src/channel/ChannelToken.d.ts
261
+ /**
262
+ * The barrier between the application and the view, declared once.
263
+ *
264
+ * A token is a name, a shape and an initial value — and no
265
+ * implementation at all. Both threads import it, which is the point:
266
+ * the module holding it has nothing in it to bundle, so an app's api
267
+ * client and domain logic never reach the render worker the way a
268
+ * shared `Store` class dragged them there.
269
+ *
270
+ * What the framework knows about a channel ends here. It diffs plain
271
+ * data and ships patches; where the observables came from — a single
272
+ * subject, or an api → repository → domain → view-model stack — is the
273
+ * application's business and the framework cannot tell the difference.
274
+ *
275
+ * export interface CatalogView {
276
+ * products: ProductRow[];
277
+ * status: 'loading' | 'ready' | 'error';
278
+ * }
279
+ * export interface CatalogCommands {
280
+ * addToCart(id: string): void;
281
+ * }
282
+ * export const Catalog = channel<CatalogView, CatalogCommands>('catalog', {
283
+ * products: [],
284
+ * status: 'loading'
285
+ * });
286
+ */
287
+ /**
288
+ * A command a component can send back across the barrier.
289
+ *
290
+ * The arguments are what cross: each is structured-cloned onto the
291
+ * owning thread, so they must be plain data. There may be as many as
292
+ * the command needs, which is why `move(from, to)` is written the way
293
+ * anyone would write it rather than as `move({ from, to })`; a command
294
+ * used to carry one payload and the second argument was dropped on the
295
+ * floor with a warning.
296
+ *
297
+ * Commands return nothing: the effect comes back as a patch, never as
298
+ * a return value, since there is no synchronous answer to be had
299
+ * across a thread.
300
+ */
301
+ type Command = (...args: never[]) => void;
302
+ /**
303
+ * The internal view of a command set: a bag of callables by name.
304
+ *
305
+ * Public signatures constrain to `object`, not to this. An application
306
+ * declares its commands as an ordinary interface —
307
+ * `interface CatalogCommands { addToCart(id: string): void }` — and an
308
+ * interface has no index signature, so it does not satisfy a
309
+ * `Record<string, …>` constraint however well it fits in spirit.
310
+ * Constraining to `object` accepts what people actually write; this
311
+ * alias is what the implementation casts to when it looks a command up
312
+ * by name.
313
+ */
314
+ type CommandMap = Record<string, Command>;
315
+ /** The declared barrier for one area of an application. */
316
+ interface ChannelToken<View extends object, Commands extends object = Record<string, never>> {
317
+ readonly name: string;
318
+ /**
319
+ * What every view key holds before the first patch arrives.
320
+ *
321
+ * Required rather than optional, so the render thread never observes
322
+ * `undefined` for a declared key. A channel that is genuinely still
323
+ * loading says so in its own shape — a `status` field — rather than
324
+ * leaving the view to infer it from an absence.
325
+ */
326
+ readonly initial: View;
327
+ /** Phantom, carrying the command types to `send`. Never read. */
328
+ readonly commands?: Commands;
329
+ }
330
+ /**
331
+ * Declares a channel.
332
+ *
333
+ * The name identifies it across the thread boundary and must be
334
+ * stable; unlike a class name it survives minification, which is why
335
+ * it is written out rather than derived.
336
+ */
337
+ declare function channel<View extends object, Commands extends object = Record<string, never>>(name: string, initial: View): ChannelToken<View, Commands>;
338
+ /** A channel written as one object: the view with its values, and the commands. */
339
+ interface ChannelSpec<View extends object, Commands extends object> {
340
+ /**
341
+ * Every view key with the value it holds before the first patch.
342
+ *
343
+ * The type of the channel's view is the type of this object, so the
344
+ * keys and their initial values are written once instead of in an
345
+ * interface and again in a literal that has to agree with it.
346
+ */
347
+ readonly view: View;
348
+ /**
349
+ * The commands, as a type rather than as implementations.
350
+ *
351
+ * Written `{} as { addToCart(id: string, quantity: number): void }`:
352
+ * the handlers live on the thread that owns the data and are passed
353
+ * to `provide`, so what belongs in the token is only their shape.
354
+ */
355
+ readonly commands?: Commands;
356
+ }
357
+ /**
358
+ * Declares a channel from one object.
359
+ *
360
+ * export const Catalog = defineChannel('catalog', {
361
+ * view: {
362
+ * products: [] as readonly ProductRow[],
363
+ * status: 'loading' as ShelfStatus
364
+ * },
365
+ * commands: {} as {
366
+ * addToCart(id: string, quantity: number): void;
367
+ * move(from: number, to: number): void;
368
+ * }
369
+ * });
370
+ *
371
+ * export type CatalogView = ViewOf<typeof Catalog>;
372
+ *
373
+ * The same token `channel()` returns, declared once instead of three
374
+ * times. `channel<View, Commands>(name, initial)` wrote the view as an
375
+ * interface, then as an initial literal that had to agree with it, and
376
+ * a key added to one and forgotten in the other was a type error in a
377
+ * third file. Here the object is the type.
378
+ *
379
+ * A field whose initial value is narrower than the type it holds is
380
+ * given the type it holds: `[]` is `never[]` and `'loading'` is
381
+ * `string` unless it is said, which is what the `as` clauses above are
382
+ * for. `ViewOf` and `CommandsOf` name the resulting types wherever the
383
+ * application used to name its own interface.
384
+ *
385
+ * `channel()` is not deprecated and keeps working exactly as it did.
386
+ * An application with interfaces it wants to keep, because they are
387
+ * shared with something else or because the initial values are built
388
+ * elsewhere, has nothing to migrate.
389
+ */
390
+ declare function defineChannel<View extends object, Commands extends object = Record<string, never>>(name: string, spec: ChannelSpec<View, Commands>): ChannelToken<View, Commands>;
391
+ /** The view type of a channel token, for an application that wants to name it. */
392
+ type ViewOf<T> = T extends ChannelToken<infer View, object> ? View : never;
393
+ /** The command type of a channel token. */
394
+ type CommandsOf<T> = T extends ChannelToken<object, infer Commands> ? Commands : never;
395
+ /**
396
+ * The keys a channel publishes.
397
+ *
398
+ * Structural in its parameter rather than generic over the token, so
399
+ * it does not have to agree with any particular command type to read
400
+ * what is only ever the initial value's shape.
401
+ */
402
+ declare function viewKeys(token: {
403
+ initial: object;
404
+ }): string[];
405
+ //#endregion
406
+ //#region src/channel/StorePatch.d.ts
407
+ type PatchPath = readonly (string | number)[];
408
+ /**
409
+ * A change to one projection, as sent from a data worker to a replica.
410
+ *
411
+ * Paths are relative to the projection's root value, so a patch is
412
+ * self-contained: the replica never needs the previous value to apply
413
+ * one, only the value it already holds.
414
+ */
415
+ type Patch = {
416
+ op: 'set';
417
+ projection: string;
418
+ path: PatchPath;
419
+ value: unknown;
420
+ } | {
421
+ op: 'delete';
422
+ projection: string;
423
+ path: PatchPath;
424
+ } | {
425
+ op: 'splice';
426
+ projection: string;
427
+ path: PatchPath;
428
+ index: number;
429
+ deleteCount: number;
430
+ items: readonly unknown[];
431
+ };
432
+ /**
433
+ * Describes how to turn `previous` into `current` for one projection.
434
+ *
435
+ * Returns an empty list when nothing changed, which is the common case
436
+ * and the reason this exists: the point of a projection is that most
437
+ * state changes do not alter it, and the ones that do usually alter a
438
+ * small part.
439
+ */
440
+ declare function diffProjection(projection: string, previous: unknown, current: unknown): Patch[];
441
+ /**
442
+ * Applies patches to a projection value, sharing structure with the
443
+ * original everywhere the patch did not reach.
444
+ *
445
+ * Nothing is mutated: bindings hold onto emitted values, so a replica
446
+ * that edited in place would change data a component already rendered.
447
+ */
448
+ declare function applyPatches(root: unknown, patches: readonly Patch[]): unknown;
449
+ declare function applyPatch(root: unknown, patch: Patch): unknown;
450
+ //#endregion
451
+ //#region src/channel/ChannelProtocol.d.ts
452
+ /**
453
+ * A two-way channel endpoint. `MessagePort` satisfies it, as does a
454
+ * test double, so nothing in the channel layer depends on Worker.
455
+ */
456
+ interface ChannelPort {
457
+ postMessage(message: unknown): void;
458
+ onmessage: ((event: {
459
+ data: unknown;
460
+ }) => void) | null;
461
+ }
462
+ /**
463
+ * Render thread → the thread that owns the channel.
464
+ *
465
+ * A command's first argument stayed `payload` when commands learned to
466
+ * take more than one, and the rest travel beside it. That is not
467
+ * tidiness: an older view talking to a newer application worker sends
468
+ * no `rest`, which reads as the one-argument call it is, and an older
469
+ * application worker ignores the field, which is exactly what it did
470
+ * before the field existed.
471
+ */
472
+ type ChannelClientMessage = {
473
+ type: 'channel:sync';
474
+ } | {
475
+ type: 'channel:command';
476
+ command: string;
477
+ payload: unknown;
478
+ rest?: unknown[];
479
+ };
480
+ /** The owning thread → render thread. */
481
+ type ChannelHostMessage = {
482
+ type: 'channel:patch';
483
+ patches: Patch[];
484
+ } | {
485
+ type: 'channel:error';
486
+ message: string;
487
+ stack?: string;
488
+ };
489
+ declare function isChannelClientMessage(value: unknown): value is ChannelClientMessage;
490
+ declare function isChannelHostMessage(value: unknown): value is ChannelHostMessage;
491
+ //#endregion
492
+ //#region src/channel/ChannelReplica.d.ts
493
+ /**
494
+ * The render thread's end of a channel.
495
+ *
496
+ * It runs none of the application's logic. It holds the latest value
497
+ * of each view key and forwards commands, and that asymmetry is the
498
+ * point — the work stays on the thread that owns the data.
499
+ *
500
+ * Every key is an `InputCell`: a component reads it, binds it, and
501
+ * cannot write it, which is the same contract a prop has. Whether a
502
+ * value arrived from a parent or from across the barrier makes no
503
+ * difference to the component that reads it, so it is not worth a
504
+ * second name.
505
+ */
506
+ declare class ChannelReplica<View extends object, Commands extends object> {
507
+ private readonly token;
508
+ private readonly port;
509
+ private readonly cells;
510
+ private readonly commandProxy;
511
+ private errorListener;
512
+ private pending;
513
+ private scheduleFlush;
514
+ constructor(token: ChannelToken<View, Commands>, port: ChannelPort);
515
+ /** The view keys, each an `InputCell` to read or bind. */
516
+ get view(): { readonly [K in keyof View]: InputCell<View[K]>; };
517
+ /**
518
+ * The channel's commands, typed as declared.
519
+ *
520
+ * Fire and forget: the effect comes back as a patch, never a return
521
+ * value. There is no synchronous answer across a thread, and
522
+ * pretending otherwise would invite code that cannot work.
523
+ */
524
+ get send(): Commands;
525
+ private readonly viewProxy;
526
+ private createCommandProxy;
527
+ /** Receives errors reported by the thread that owns the channel. */
528
+ onError(listener: ((message: string, stack?: string) => void) | null): void;
529
+ private receive;
530
+ private report;
531
+ /**
532
+ * Defers patch application to the next frame.
533
+ *
534
+ * A chatty application thread can deliver many patches between two
535
+ * frames. Applied on arrival each one pushes a value through the
536
+ * bindings watching it, rebuilding a subtree once per patch when
537
+ * only the last state is ever drawn. Queued, a burst costs one pass.
538
+ */
539
+ deferPatches(scheduleFlush: () => void): void;
540
+ get hasPendingPatches(): boolean;
541
+ flush(): void;
542
+ /**
543
+ * Applies a batch, emitting once per affected key.
544
+ *
545
+ * Grouping matters: a batch touching one key three times must not
546
+ * push three values through the bindings watching it.
547
+ */
548
+ applyPatches(patches: readonly Patch[]): void;
549
+ private post;
550
+ dispose(): void;
551
+ }
552
+ //#endregion
553
+ //#region src/FunctionComponent.d.ts
554
+ /**
555
+ * What a functional component can ask of the framework while its body
556
+ * runs. It is the function's half of what `@Inject`, `onMount()` and
557
+ * `onUnmount()` give a class.
558
+ */
559
+ interface ComponentContext {
560
+ /**
561
+ * The runtime service of this class: overlays, focus, find, the
562
+ * clipboard, media, animation.
563
+ *
564
+ * Services stay on this thread and are simply called. Application
565
+ * state comes through `channel` instead.
566
+ */
567
+ inject<S extends object>(ServiceClass: new () => S): S;
568
+ /**
569
+ * The channel declared by `token`: `view` keys to read or bind, and
570
+ * `send` to issue a command.
571
+ */
572
+ channel<V extends object, C extends object>(token: ChannelToken<V, C>): ChannelReplica<V, C>;
573
+ /**
574
+ * Runs once after the component's nodes exist and its bindings are
575
+ * connected. Must be called while the component function runs.
576
+ */
577
+ onMount(hook: () => void): void;
578
+ /**
579
+ * Runs once when the component leaves the tree, before its
580
+ * subscriptions are torn down. Must be called while the component
581
+ * function runs.
582
+ */
583
+ onUnmount(hook: () => void): void;
584
+ /**
585
+ * Follows a stream for as long as the component is in the tree.
586
+ *
587
+ * A component that has to *act* on a value rather than draw it,
588
+ * telling the audio element to load a track, asking a channel for the
589
+ * page a url names, writing a scroll offset somewhere, subscribes, and
590
+ * something has to unsubscribe. Every screen in both applications
591
+ * wrote that pair by hand, and one of them had grown a `Subscription`
592
+ * bag to hold four of them.
593
+ *
594
+ * ctx.effect(queue.view.current, track => audio.load(track.stream));
595
+ *
596
+ * The subscription is the host's and is torn down with the component,
597
+ * after `onUnmount` has run, in the order the host tears down every
598
+ * subscription it opened on the component's behalf. It is handed back
599
+ * for the rare case that wants to stop early; nothing has to hold it.
600
+ *
601
+ * Unlike `onMount` and `onUnmount` this may be called after the body,
602
+ * from a callback the component registered, since what it registers
603
+ * is a teardown rather than a hook that has already been run past.
604
+ */
605
+ effect<T>(source: Observable<T>, run: (value: T) => void): Subscription;
606
+ /**
607
+ * A cell holding a node's box, with the modifier that fills it.
608
+ *
609
+ * Turning a pointer position into a fraction of a track, or a drag
610
+ * into a seek, starts with knowing where the element is, and until
611
+ * now that meant declaring `new BehaviorSubject<LayoutBox>` with a
612
+ * zero box in it and passing it to `measure`. This is that, named,
613
+ * and it drops a report of a box that has not moved.
614
+ *
615
+ * const track = ctx.bounds();
616
+ * <box modifiers={[track.modifier]} onPointerDown={e => seek(e.x - track.value.x)} />
617
+ */
618
+ bounds(label?: string): BoundsCell;
619
+ }
620
+ /**
621
+ * The inputs a functional component receives: one host-owned cell per
622
+ * declared member, and for a member typed as a function, an output.
623
+ *
624
+ * The parent supplies values or Observables; the host feeds them into
625
+ * these cells and keeps feeding them when the parent's values change,
626
+ * so the function can run exactly once, like a class `render()`, and
627
+ * still follow its parent. An output is fired with
628
+ * `inputs.onChange.emit(next)`, and a parent may pass a handler or
629
+ * `into(subject)` for it.
630
+ *
631
+ * Every declared input is a cell, whether or not the parent passed
632
+ * anything: the record hands one out on first access and the host keeps
633
+ * feeding it, so an optional input that was omitted is a live cell
634
+ * holding `undefined`, and it takes a value if the parent starts
635
+ * passing one. What it is *not* is a value, so binding it straight to a
636
+ * property writes `undefined` there and the property draws as though it
637
+ * had never been set. `input(inputs.name, fallback)` gives it a
638
+ * default, and `select(inputs.name, ...)` projects one.
639
+ * `Input.optional.spec.ts` is that case written down.
640
+ */
641
+ type Inputs<P> = { readonly [K in keyof P]-?: InputCell<P[K]>; };
642
+ /**
643
+ * A component written as a function.
644
+ *
645
+ * function Counter(inputs: Inputs<{ label?: string }>, ctx: ComponentContext) {
646
+ * const label = input(inputs.label, 'Count');
647
+ * const count = state(0);
648
+ * const store = ctx.inject(DemoStore);
649
+ * return Row(Text({ text: label }), Button({ onClick: () => count.value++ }));
650
+ * }
651
+ *
652
+ * The body is the component's `render()`: it runs once per instance,
653
+ * and everything dynamic in the returned tree is an Observable. Local
654
+ * state is `internalState()` cells created in the body.
655
+ */
656
+ type FunctionComponent<P = {}> = (inputs: Inputs<P>, context: ComponentContext) => UiChild;
657
+ type ClassComponent = new () => Component;
658
+ /**
659
+ * Anything `createComponent` (and JSX) can mount. The function half is
660
+ * loose on purpose: a function's own `Inputs<P>` parameter is what
661
+ * `ComponentProps` reads its props from.
662
+ */
663
+ type ComponentType = ClassComponent | ((inputs: any, context: ComponentContext) => UiChild);
664
+ type IsAny<T> = 0 extends 1 & T ? true : false;
665
+ type CellValue<C> = C extends InputCell<infer T> ? T : never;
666
+ /** The keys of a cell record whose value may be undefined; the parent may omit those. */
667
+ type OptionalCellKeys<I> = { [K in keyof I]: undefined extends CellValue<I[K]> ? K : never; }[keyof I];
668
+ /**
669
+ * The props a parent may pass for a record of input cells: each one a
670
+ * value or an Observable of it. A cell that admits `undefined` is
671
+ * optional; every other one is required.
672
+ */
673
+ type PropsForCells<I> = [keyof I] extends [never] ? NoProps : { [K in OptionalCellKeys<I>]?: Passable<CellValue<I[K]>>; } & { [K in Exclude<keyof I, OptionalCellKeys<I>>]: Passable<CellValue<I[K]>>; };
674
+ /**
675
+ * What a parent may pass for one cell: a value or an Observable of it,
676
+ * and for an output, the handler itself or an `into(subject)` target
677
+ * that receives what the child emits.
678
+ */
679
+ type Passable<T> = NonNullable<T> extends ((first: infer V, ...rest: never[]) => void) ? Reactive<T> | OutputTarget<V> : Reactive<T>;
680
+ /**
681
+ * A component that declares no props accepts none: `{}` passes, anything
682
+ * else is an excess property error. A bare `{}` type would accept
683
+ * anything, and an index signature would swallow JSX's `key`, so this
684
+ * is an object type with one optional phantom member that can never be
685
+ * set.
686
+ */
687
+ type NoProps = {
688
+ readonly __noProps?: never;
689
+ };
690
+ /** The `input()` fields of a class component, as the props its parent may pass. */
691
+ type InputFields<I> = { [K in keyof I as I[K] extends InputCell<any> ? K : never]: I[K]; };
692
+ /**
693
+ * The props a parent passes to a component.
694
+ *
695
+ * For a class, its `@Input() x = input(default)` fields, all optional
696
+ * because each has a default. For a function, the cells of its first
697
+ * parameter. A misspelled prop is an excess property error; a prop
698
+ * whose cell holds a `string` rejects a `number` or an
699
+ * `Observable<number>`.
700
+ */
701
+ type ComponentProps<C> = C extends ClassComponent ? Partial<PropsForCells<InputFields<InstanceType<C>>>> : C extends ((inputs: infer I, ...rest: any[]) => UiChild) ? IsAny<I> extends true ? Record<string, unknown> : unknown extends I ? NoProps : [I] extends [undefined] ? NoProps : PropsForCells<I> : never;
702
+ type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K; }[keyof T];
703
+ /**
704
+ * The trailing arguments of `createComponent`: props may be omitted
705
+ * only when the component requires none of them.
706
+ */
707
+ type ComponentArgs<C> = RequiredKeys<ComponentProps<C>> extends never ? [inputs?: ComponentProps<C>, key?: string | number] : [inputs: ComponentProps<C>, key?: string | number];
708
+ /**
709
+ * Whether a component is a class (extends Component) rather than a
710
+ * function. Arrow functions have no prototype; a plain function's
711
+ * prototype is not a Component.
712
+ */
713
+ declare function isClassComponent(component: ComponentType): component is ClassComponent;
714
+ //#endregion
715
+ export { bounds as A, output as B, CommandMap as C, defineChannel as D, channel as E, OutputTarget as F, internalState as H, ReadableCell as I, input as L, EmitValue as M, InputCell as N, viewKeys as O, OutputCell as P, into as R, Command as S, ViewOf as T, Component as U, InternalState as V, applyPatch as _, ComponentType as a, ChannelSpec as b, isClassComponent as c, ChannelHostMessage as d, ChannelPort as f, PatchPath as g, Patch as h, ComponentProps as i, EmitArgs as j, BoundsCell as k, ChannelReplica as l, isChannelHostMessage as m, ComponentArgs as n, FunctionComponent as o, isChannelClientMessage as p, ComponentContext as r, Inputs as s, ClassComponent as t, ChannelClientMessage as u, applyPatches as v, CommandsOf as w, ChannelToken as x, diffProjection as y, isOutputTarget as z };
716
+ //# sourceMappingURL=FunctionComponent-DAyf5HQ6.d.ts.map