@effected/cli 0.22.2 → 0.23.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.
package/ui.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as Cli from "@effected/cli";
2
- import { Console, Context, Effect, Option, PubSub, Scope, Stream } from "effect";
2
+ import { Console, Context, Effect, Exit, Layer, Option, PubSub, Scope, Stream } from "effect";
3
3
  import { Param } from "effect/cli";
4
4
  import { ReactElement, ReactNode } from "react";
5
+ import { Writable } from "node:stream";
5
6
  import { Key } from "ink";
6
7
  import { ColorLevel } from "@effected/env";
7
8
  /**
@@ -177,6 +178,120 @@ interface LiveHandle<S> {
177
178
  */
178
179
  readonly close: Effect.Effect<void>;
179
180
  }
181
+ /**
182
+ * Options for `CliUi.liveDetached`: those of `CliUi.live`, with the events given as a `PubSub` or a stream, and the
183
+ * render given directly or as an effect that makes it.
184
+ *
185
+ * @typeParam R - the services a `render` effect reads, which the `env` layer provides
186
+ *
187
+ * @public
188
+ */
189
+ interface DetachedLiveOptions<E, S, R = never> extends Omit<LiveOptions<E, S>, "events" | "render"> {
190
+ /**
191
+ * The events to fold. A `PubSub` is subscribed before `liveDetached` returns, in the view's own scope, so an event
192
+ * published right after the call is seen and `close` folds every message still queued. A stream is read as
193
+ * `CliUi.live` reads one.
194
+ */
195
+ readonly events: PubSub.PubSub<E> | Stream.Stream<E>;
196
+ /**
197
+ * Draw the state, as `CliUi.live`'s `render`; or an effect that makes that function, run once under `env` before
198
+ * the view is set up, so a render that needs an environment service (`CliLinks` to build links, `TerminalEnv` for
199
+ * the terminal's capabilities) reads it from the layer the view already builds rather than from a second build.
200
+ *
201
+ * @remarks
202
+ * A render effect that dies is the view's setup failing: `handle` rejects, `close` resolves, and `exit` carries
203
+ * the die, as for an `env` layer that dies. It can return a `CliUi.lazyView`.
204
+ */
205
+ readonly render: LiveOptions<E, S>["render"] | Effect.Effect<LiveOptions<E, S>["render"], never, R>;
206
+ }
207
+ /**
208
+ * A live view running outside Effect, from `CliUi.liveDetached`.
209
+ *
210
+ * @public
211
+ */
212
+ interface DetachedLive<S> {
213
+ /**
214
+ * The view's handle once `CliUi.live` has set it up (it loads Ink asynchronously). Rejects if the view could not be
215
+ * set up; the rejection is handled, so a host that never awaits it sees no unhandled rejection.
216
+ */
217
+ readonly handle: Promise<LiveHandle<S>>;
218
+ /**
219
+ * The handle's `printAbove`: print one line above the frame and say whether it did. `false`, with nothing written,
220
+ * before the handle is set up, whenever no run's frame is mounted, and from the moment `close` is called, so the
221
+ * host writes the line itself.
222
+ */
223
+ readonly printAbove: (stream: "stdout" | "stderr", line: string) => boolean;
224
+ /**
225
+ * End the view and release it.
226
+ *
227
+ * @remarks
228
+ * In order: `printAbove` stops printing; the view's setup is awaited (one that died counts as settled); the
229
+ * handle's `close` folds every message still queued in the subscription and commits the last run; then the view's
230
+ * scope is closed, which unsubscribes and unmounts. Shut the `PubSub` down only after the returned promise
231
+ * resolves: a `PubSub.shutdown` before it drops the events the view has not taken yet.
232
+ *
233
+ * Idempotent: every call returns the same promise. It never rejects; how the view ended is `exit`.
234
+ */
235
+ readonly close: () => Promise<void>;
236
+ /**
237
+ * How the view ended: a success when its events ended or `close` ended them, the failure it died of (a `reduce`
238
+ * that threw, a stream that died, a setup that failed), or an interrupt when its scope closed under it.
239
+ */
240
+ readonly exit: Promise<Exit.Exit<void>>;
241
+ }
242
+ /**
243
+ * Options for `CliUi.routeStream`.
244
+ *
245
+ * @public
246
+ */
247
+ interface RouteStreamOptions {
248
+ /** Which of the printer's streams the lines belong on. */
249
+ readonly stream: "stdout" | "stderr";
250
+ /**
251
+ * The most characters held back waiting for a line break; 8192 by default. Past it, the held text is offered as a
252
+ * line of its own, so a writer that never ends a line cannot grow the buffer without bound.
253
+ */
254
+ readonly maxHeld?: number | undefined;
255
+ /**
256
+ * What a line holding a carriage return is printed as: `keep` (the default) prints it as written, and `last` prints
257
+ * only the text after its last `\r`, which is what a terminal would have shown of a progress bar redrawn in place.
258
+ * A CRLF line ending is a line ending either way. A line written to the original stream is always written as it came.
259
+ */
260
+ readonly carriageReturn?: "keep" | "last" | undefined;
261
+ /**
262
+ * Called with every piece of text written, decoded, before it is routed: a tap for a recorder.
263
+ *
264
+ * @remarks
265
+ * Every write reaches it, a write of nothing but escape sequences (a cursor hide, a clear-screen) included, though
266
+ * such a write is never offered to the printer. A write holding only the first bytes of a character reaches it when
267
+ * the character completes, or as U+FFFD at `flush`.
268
+ */
269
+ readonly onText?: ((text: string) => void) | undefined;
270
+ }
271
+ /**
272
+ * A stream routed line by line through a printer, from `CliUi.routeStream`.
273
+ *
274
+ * @public
275
+ */
276
+ interface RoutedStream {
277
+ /**
278
+ * The stream to hand to the writer in place of the original. It reports the original's `isTTY`, `columns`, `rows`,
279
+ * `getColorDepth`, `hasColors` and `getWindowSize`, where the original has them, so a library that sizes its output
280
+ * to the terminal still sees one.
281
+ */
282
+ readonly writable: Writable;
283
+ /**
284
+ * Turn routing on (the default) or off. Turned off, the text held waiting for a line break is written to the
285
+ * original, and every later write goes straight to it, decoded, until routing is turned back on.
286
+ */
287
+ readonly route: (on: boolean) => void;
288
+ /**
289
+ * Offer the text held waiting for a line break to the printer now, or write it to the original if it declines.
290
+ * It ends the decoding too: a character split across writes whose second half has not arrived is written as
291
+ * U+FFFD. Ending `writable` (`end()`) flushes the same way, so a last line with no line break is never lost.
292
+ */
293
+ readonly flush: () => void;
294
+ }
180
295
  /**
181
296
  * What the kit's hooks read in a tree the kit did not mount: the theme, the glyph set, and optionally the size.
182
297
  *
@@ -200,6 +315,12 @@ interface UiContextValue {
200
315
  readonly columns: number;
201
316
  readonly rows: number;
202
317
  };
318
+ /**
319
+ * How a `DocView` under it shows an absolute path: `CliUi.context` sets it from the run's `displayPath`
320
+ * (`CliEnvOptions.displayPath`), so a frame shows a path as `Doc.print` and a live view's `final` do. Absent, a
321
+ * nested provider keeps the one above it, and with none the path is shown as given.
322
+ */
323
+ readonly displayPath?: ((absolute: string) => string) | undefined;
203
324
  /**
204
325
  * Whether the GitHub Actions runner reads the output (`CliUi.context` sets it from `CurrentRuntimeEnv`, when one is
205
326
  * provided): a `DocView` under it neutralizes any line its data would turn into a workflow command. `false` opts the
@@ -452,6 +573,82 @@ export declare class CliUi {
452
573
  * @param options - the events, the fold, the drawing, and what starts and ends a run
453
574
  */
454
575
  static readonly live: <E, S>(options: LiveOptions<E, S>) => Effect.Effect<LiveHandle<S>, never, Scope.Scope | Cli.CliTheme>;
576
+ /**
577
+ * A live view hosted by code that is not built on Effect: a test runner's reporter, a bundler plugin, a build hook.
578
+ *
579
+ * @remarks
580
+ * It runs `CliUi.live` in a fiber of its own, under `env` and in a scope of its own, and returns at once. A `PubSub`
581
+ * given as `events` is subscribed before it returns, so the first event published after the call is not lost.
582
+ * `env` is the environment the view reads, built by the host, which owns the platform: under Node,
583
+ * `CliEnv.layer().pipe(Layer.provide(NodeServices.layer))`; a test gives `CliEnv.layerTest` with a session's layer.
584
+ *
585
+ * End it with `close`, and only then shut the `PubSub` down: `close` folds the subscription's queued tail, commits
586
+ * the last run and releases the scope, and a shutdown before it drops that tail. `close` never rejects; `exit`
587
+ * says how the view ended. Forward output the host did not write through `printAbove`, or hand
588
+ * `CliUi.routeStream` the detached view's `printAbove` as its printer.
589
+ *
590
+ * A render that needs an environment service is given as an effect that makes the render function: it runs once
591
+ * under `env`, before the view is set up, so `CliLinks` or `TerminalEnv` come from the same build the view reads and
592
+ * the layer is not built a second time to resolve them. `env` must provide what that effect reads.
593
+ *
594
+ * Inside an Effect program, use `CliUi.live` in a scope instead.
595
+ *
596
+ * @example
597
+ * ```ts
598
+ * const view = CliUi.liveDetached(
599
+ * {
600
+ * ...options,
601
+ * render: Effect.gen(function* () {
602
+ * const links = yield* CliLinks
603
+ * return (state: State, frame: number) => drawWith(links, state, frame)
604
+ * }),
605
+ * },
606
+ * CliEnv.layer().pipe(Layer.provide(NodeServices.layer)),
607
+ * )
608
+ * ```
609
+ *
610
+ * @param options - the live view's options, with the events as a `PubSub` or a stream and the render given directly
611
+ * or as an effect that makes it
612
+ * @param env - the environment the view runs under, providing what a render effect reads
613
+ */
614
+ static readonly liveDetached: <E, S, R = never>(options: DetachedLiveOptions<E, S, R>, env: Layer.Layer<Cli.CliTheme | NoInfer<R>>) => DetachedLive<S>;
615
+ /**
616
+ * A stream to hand to a writer in place of a terminal stream, which routes what it writes line by line through
617
+ * `printer`, usually a live view's `printAbove`, and writes whatever the printer declines to `original`.
618
+ *
619
+ * @remarks
620
+ * For output the program does not write itself: a child process's stderr, or the streams of a test runner or a
621
+ * bundler running in-process. Each complete line is offered to `printer`; a `false` (no frame mounted) writes it,
622
+ * with its line break, to `original`. Text without a line break yet is held until one comes, until `flush`, or
623
+ * until it passes `maxHeld` characters, when it is offered as a line of its own. Bytes are decoded as UTF-8 across
624
+ * writes, so a character split between two writes arrives whole.
625
+ *
626
+ * Everything written to `original` is the decoded text, so a character split across two writes, by whichever path,
627
+ * arrives once and whole. A write that is nothing but escape sequences (a cursor hide, a clear-screen) goes
628
+ * straight to `original` when no partial line is held, since there is nothing to print above a frame. Ending
629
+ * `writable` flushes the held text, so a writer's last line with no line break is printed, not lost. A line's own cursor
630
+ * movements still tear the frame; `carriageReturn: "last"` keeps only what a progress bar redrawn with `\r` would
631
+ * have shown.
632
+ *
633
+ * The returned `writable` reports the original's `isTTY`, `columns`, `rows`, `getColorDepth`, `hasColors` and
634
+ * `getWindowSize` where the original has them. A printer that throws fails the write. Node only, as `./ui` is.
635
+ *
636
+ * @example
637
+ * ```ts
638
+ * const view = CliUi.liveDetached(options, env)
639
+ * const routed = CliUi.routeStream(process.stderr, view.printAbove, { stream: "stderr" })
640
+ * runner.errorStream = routed.writable
641
+ * // ...at the end
642
+ * routed.flush()
643
+ * await view.close()
644
+ * ```
645
+ *
646
+ * @param original - the stream the writer would have written to
647
+ * @param printer - where each line is offered first: `true` when it printed it, `false` with nothing written
648
+ * @param options - the printer's stream, the hold limit, carriage returns and a text tap
649
+ * @throws RangeError when `maxHeld` is not a non-negative integer
650
+ */
651
+ static readonly routeStream: (original: NodeJS.WritableStream, printer: (stream: "stdout" | "stderr", line: string) => boolean, options: RouteStreamOptions) => RoutedStream;
455
652
  /**
456
653
  * Run `screen` from a handler when the run is interactive; otherwise answer with `otherwise`, or fail with
457
654
  * `NotInteractive` when there is none.
@@ -908,7 +1105,7 @@ interface DocViewProps {
908
1105
  /**
909
1106
  * The render context. Omitted, it is built from the tree's theme (`useTheme`): its colour, its `paint` (token
910
1107
  * overrides included) and its glyphs, the width `useTerminalSize().columns`, a human audience, links off, the
911
- * identity `displayPath`, and `neutralizeWorkflowCommands` when the tree is under the GitHub Actions runner. Given,
1108
+ * run's `displayPath` (the identity outside `CliRuntime.main`'s `env`), and `neutralizeWorkflowCommands` when the tree is under the GitHub Actions runner. Given,
912
1109
  * it replaces that context entirely, neutralizing included (set `neutralizeWorkflowCommands` on it when the runner
913
1110
  * reads the output), and the view needs no provider.
914
1111
  */
@@ -1786,5 +1983,5 @@ export declare const Styled: (props: StyledProps) => ReactElement;
1786
1983
  * @public
1787
1984
  */
1788
1985
  export declare const useTerminalSize: () => TerminalSize;
1789
- export type { Binding, CliUiFallbackOptions, CliUiPromptOptions, CliUiRunOptions, ConfirmAction, ConfirmInitOptions, ConfirmResult, ConfirmScreenOptions, ConfirmState, ConfirmToggle, ConfirmViewProps, DocViewProps, InkTextProps, KeyHelpProps, KeyHelpRow, KeyName, LiveHandle, LiveOptions, MultiSelectAction, MultiSelectInitOptions, MultiSelectItem, MultiSelectScreenOptions, MultiSelectSection, MultiSelectState, MultiSelectViewProps, Screen, ScreenControl, SelectAction, SelectChoice, SelectInitOptions, SelectScreenOptions, SelectState, SelectViewProps, StyledProps, Tab, TabsAction, TabsProps, TerminalSize, TextInputInitOptions, TextInputScreenOptions, TextInputState, TextInputViewProps, ToggleViewProps, UiContextValue, UiProviderProps, UiStreamsShape, UseKeysOptions, ViewportMove, ViewportRow, ViewportState, ViewportViewProps };
1986
+ export type { Binding, CliUiFallbackOptions, CliUiPromptOptions, CliUiRunOptions, ConfirmAction, ConfirmInitOptions, ConfirmResult, ConfirmScreenOptions, ConfirmState, ConfirmToggle, ConfirmViewProps, DetachedLive, DetachedLiveOptions, DocViewProps, InkTextProps, KeyHelpProps, KeyHelpRow, KeyName, LiveHandle, LiveOptions, MultiSelectAction, MultiSelectInitOptions, MultiSelectItem, MultiSelectScreenOptions, MultiSelectSection, MultiSelectState, MultiSelectViewProps, RouteStreamOptions, RoutedStream, Screen, ScreenControl, SelectAction, SelectChoice, SelectInitOptions, SelectScreenOptions, SelectState, SelectViewProps, StyledProps, Tab, TabsAction, TabsProps, TerminalSize, TextInputInitOptions, TextInputScreenOptions, TextInputState, TextInputViewProps, ToggleViewProps, UiContextValue, UiProviderProps, UiStreamsShape, UseKeysOptions, ViewportMove, ViewportRow, ViewportState, ViewportViewProps };
1790
1987
  //# sourceMappingURL=ui.d.ts.map