@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/CliEnv.js +2 -1
- package/CliFailure.js +15 -29
- package/CliFormat.js +69 -0
- package/CliLog.js +38 -16
- package/CliRuntime.js +70 -48
- package/Fmt.js +123 -2
- package/Render.js +5 -2
- package/index.d.ts +272 -5
- package/index.js +2 -1
- package/internal/causeMessage.js +57 -0
- package/internal/displayPath.js +46 -0
- package/internal/displayWidth.js +11 -3
- package/internal/failureTarget.js +2 -1
- package/internal/inspect.js +40 -0
- package/package.json +7 -5
- package/ui/CliUi.js +86 -14
- package/ui/CliUiDetached.js +36 -0
- package/ui/CliUiLive.js +6 -9
- package/ui/CliUiRouteStream.js +72 -0
- package/ui/DocView.js +7 -4
- package/ui/UiProvider.js +4 -1
- package/ui/internal/UiProviders.js +19 -1
- package/ui/internal/inkChalk.js +24 -34
- package/ui/internal/lineRouter.js +70 -0
- package/ui/testing/CliUiTest.js +4 -1
- package/ui.d.ts +200 -3
package/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Array as Array$1, Cause, Context, Duration, Effect, FileSystem, Layer, LogLevel, Logger, MutableRef, Option, Path, Runtime, Schema, Stdio, Terminal } from "effect";
|
|
2
2
|
import { Audience, AudienceKind, ColorLevel, CurrentRuntimeEnv, RuntimeEnv, TerminalEnv } from "@effected/env";
|
|
3
3
|
import { CliError, CliOutput, Command, Flag, Param, Prompt } from "effect/cli";
|
|
4
|
+
import { Distribution } from "@effected/engine";
|
|
4
5
|
import { ConfigValidationError } from "@effected/config-file";
|
|
5
6
|
declare const Cancelled_base: Schema.Class<Cancelled, Schema.TaggedStruct<"Cancelled", {
|
|
6
7
|
readonly reason: Schema.Literals<readonly ["escape", "interrupt"]>;
|
|
@@ -1847,6 +1848,24 @@ export declare class CliLog {
|
|
|
1847
1848
|
* cannot lower it below the level the layer installed, because Effect has already dropped those records.
|
|
1848
1849
|
*/
|
|
1849
1850
|
static readonly Level: Context.Reference<LogLevel.LogLevel>;
|
|
1851
|
+
/**
|
|
1852
|
+
* Read a log level the way `CliLog.layer` reads its environment variable: `Option.some` of the level, or
|
|
1853
|
+
* `Option.none()` when the text is not one.
|
|
1854
|
+
*
|
|
1855
|
+
* @remarks
|
|
1856
|
+
* Case-insensitive, and not trimmed. The spellings are `all`, `trace`, `debug`, `info`, `warn` (or `warning`),
|
|
1857
|
+
* `error`, `fatal` and `none`. For a host that reads a level from its own flag or config and wants the same answer
|
|
1858
|
+
* the layer would give.
|
|
1859
|
+
*
|
|
1860
|
+
* @example
|
|
1861
|
+
* ```ts
|
|
1862
|
+
* CliLog.parseLevel("WARNING") // Option.some("Warn")
|
|
1863
|
+
* CliLog.parseLevel("loud") // Option.none()
|
|
1864
|
+
* ```
|
|
1865
|
+
*
|
|
1866
|
+
* @param text - the level's spelling
|
|
1867
|
+
*/
|
|
1868
|
+
static parseLevel(text: string): Option.Option<LogLevel.LogLevel>;
|
|
1850
1869
|
/**
|
|
1851
1870
|
* The whole logger set of a program: a `CliLogger` for ordinary lines plus the diagnostics sink.
|
|
1852
1871
|
*
|
|
@@ -2030,10 +2049,21 @@ interface CliEnvOptions {
|
|
|
2030
2049
|
*/
|
|
2031
2050
|
readonly formatter?: Partial<CliOutput.Formatter> | undefined;
|
|
2032
2051
|
/**
|
|
2033
|
-
*
|
|
2034
|
-
*
|
|
2052
|
+
* How an absolute path is shown: a function, or `{ relativeTo: root }` to show a path under `root` relative to it.
|
|
2053
|
+
* The identity by default.
|
|
2054
|
+
*
|
|
2055
|
+
* @remarks
|
|
2056
|
+
* It is every path the kit shows for this run: `Render.context` takes it as its default, so `Doc.print`, a
|
|
2057
|
+
* `Doc.path` or file link, and a live view's `final` document follow it, and so do the stack frames of the default
|
|
2058
|
+
* failure report. An explicit `displayPath` passed to `Doc.print` or `Render.context` still wins.
|
|
2059
|
+
*
|
|
2060
|
+
* `{ relativeTo: root }` takes an absolute `root`. A path under it is shown relative to it; `root` itself, and a path
|
|
2061
|
+
* outside it, are shown absolute, since a `../..` chain says less than the absolute path. It uses the `Path` service
|
|
2062
|
+
* when the platform provides one (as `CliRuntime.main`'s does), and otherwise a plain prefix test on `/` and `\`.
|
|
2035
2063
|
*/
|
|
2036
|
-
readonly displayPath?: ((absolute: string) => string) |
|
|
2064
|
+
readonly displayPath?: ((absolute: string) => string) | {
|
|
2065
|
+
readonly relativeTo: string;
|
|
2066
|
+
} | undefined;
|
|
2037
2067
|
/**
|
|
2038
2068
|
* Which frames of a defect's stack the default failure report shows: `app`, the default, leaves out every
|
|
2039
2069
|
* `node_modules` frame (Effect's and any other dependency's) and the runtime's own (every `node:` frame and every
|
|
@@ -2310,7 +2340,8 @@ interface CliFailureOptions {
|
|
|
2310
2340
|
* one line `interrupted`.
|
|
2311
2341
|
*
|
|
2312
2342
|
* All text goes through the document, so a control character in a message or a stack frame never reaches the terminal.
|
|
2313
|
-
*
|
|
2343
|
+
* Building it never throws: an error whose `_tag`, `message`, `stack`, `cause` or `issue` getter throws is drawn without
|
|
2344
|
+
* that part (an unreadable `stack` as `no stack`, an unreadable `cause` ending the chain there). Render it with `Render.context` and `Render.plain`, `ansi`, `markdown` or `githubLog`, or `Doc.print` it.
|
|
2314
2345
|
*
|
|
2315
2346
|
* @public
|
|
2316
2347
|
*/
|
|
@@ -2324,6 +2355,93 @@ export declare class CliFailure {
|
|
|
2324
2355
|
*/
|
|
2325
2356
|
static readonly toDoc: (cause: Cause.Cause<unknown>, options?: CliFailureOptions) => Document;
|
|
2326
2357
|
}
|
|
2358
|
+
/**
|
|
2359
|
+
* An output format a `--format` flag can offer: `auto` (the renderer the audience gets, as `Doc.print` picks it),
|
|
2360
|
+
* one of the `Doc` renderers by name, or `json`, the command's data rather than its document.
|
|
2361
|
+
*
|
|
2362
|
+
* @public
|
|
2363
|
+
*/
|
|
2364
|
+
type CliFormatName = "auto" | "plain" | "ansi" | "markdown" | "githubLog" | "json";
|
|
2365
|
+
/**
|
|
2366
|
+
* Options for {@link CliFormat.flag}.
|
|
2367
|
+
*
|
|
2368
|
+
* @public
|
|
2369
|
+
*/
|
|
2370
|
+
interface CliFormatFlagOptions<F extends CliFormatName> {
|
|
2371
|
+
/** The format when the flag is not given: `auto` when it is offered, otherwise the first one. */
|
|
2372
|
+
readonly default?: F | undefined;
|
|
2373
|
+
/** The flag's name; `format` by default. */
|
|
2374
|
+
readonly name?: string | undefined;
|
|
2375
|
+
/** The flag's description in help; one naming the offered formats by default. */
|
|
2376
|
+
readonly description?: string | undefined;
|
|
2377
|
+
}
|
|
2378
|
+
/**
|
|
2379
|
+
* What {@link CliFormat.print} prints: the command's document and, for `json`, its data.
|
|
2380
|
+
*
|
|
2381
|
+
* @public
|
|
2382
|
+
*/
|
|
2383
|
+
interface CliFormatOutput {
|
|
2384
|
+
/** The document, built only when a document format is chosen. */
|
|
2385
|
+
readonly doc: () => Document;
|
|
2386
|
+
/** The data `json` prints, built only when `json` is chosen. A `json` format with no `json` here is a defect. */
|
|
2387
|
+
readonly json?: (() => unknown) | undefined;
|
|
2388
|
+
}
|
|
2389
|
+
/**
|
|
2390
|
+
* Options for {@link CliFormat.print}.
|
|
2391
|
+
*
|
|
2392
|
+
* @public
|
|
2393
|
+
*/
|
|
2394
|
+
interface CliFormatPrintOptions {
|
|
2395
|
+
/** The stream to write to; stdout by default. */
|
|
2396
|
+
readonly stream?: "stdout" | "stderr" | undefined;
|
|
2397
|
+
/** The JSON indent, in spaces; `2` by default, `0` for one line. */
|
|
2398
|
+
readonly indent?: number | undefined;
|
|
2399
|
+
}
|
|
2400
|
+
/**
|
|
2401
|
+
* A `--format` flag and the one print that honours it, so a command builds its report once as a `Doc` and its data
|
|
2402
|
+
* once as a value, and never writes per-format output by hand.
|
|
2403
|
+
*
|
|
2404
|
+
* @remarks
|
|
2405
|
+
* The flag is core's `Flag.Literals` with the kit's names, default and description; the print hands every document
|
|
2406
|
+
* format to `Doc.print` and writes `json` itself.
|
|
2407
|
+
*
|
|
2408
|
+
* ```ts
|
|
2409
|
+
* const format = CliFormat.flag(["auto", "markdown", "json"])
|
|
2410
|
+
* const doctor = Command.make("doctor", { format }, ({ format }) =>
|
|
2411
|
+
* CliFormat.print(format, { doc: () => report(results), json: () => results }),
|
|
2412
|
+
* )
|
|
2413
|
+
* ```
|
|
2414
|
+
*
|
|
2415
|
+
* @public
|
|
2416
|
+
*/
|
|
2417
|
+
export declare class CliFormat {
|
|
2418
|
+
private constructor();
|
|
2419
|
+
/**
|
|
2420
|
+
* A `--format` flag offering `formats`.
|
|
2421
|
+
*
|
|
2422
|
+
* @remarks
|
|
2423
|
+
* Its default is `options.default`, else `auto` when it is offered, else the first format. Any other value is a
|
|
2424
|
+
* usage error from core's parser, naming the accepted ones.
|
|
2425
|
+
*
|
|
2426
|
+
* @param formats - the formats the command offers, at least one
|
|
2427
|
+
* @param options - the default, the flag's name and its description
|
|
2428
|
+
*/
|
|
2429
|
+
static readonly flag: <const F extends CliFormatName>(formats: readonly [F, ...ReadonlyArray<F>], options?: CliFormatFlagOptions<F>) => Flag.Flag<F>;
|
|
2430
|
+
/**
|
|
2431
|
+
* Print a command's output in `format`.
|
|
2432
|
+
*
|
|
2433
|
+
* @remarks
|
|
2434
|
+
* `json` writes `output.json()` as JSON, one `Console.log` (or `Console.error` for stderr), indented by `indent`.
|
|
2435
|
+
* Under GitHub Actions a `##[` inside it is written as the JSON escape `#\u0023[`, which decodes to the same text,
|
|
2436
|
+
* so the runner cannot read it as a command. A value JSON cannot hold (a cycle, a bigint) is a defect. Every other
|
|
2437
|
+
* format prints `output.doc()` with `Doc.print`, which renders it as `auto` or the named renderer would.
|
|
2438
|
+
*
|
|
2439
|
+
* @param format - the chosen format, usually the value of {@link CliFormat.flag}
|
|
2440
|
+
* @param output - the document and the data
|
|
2441
|
+
* @param options - the stream and the JSON indent
|
|
2442
|
+
*/
|
|
2443
|
+
static readonly print: (format: CliFormatName, output: CliFormatOutput, options?: CliFormatPrintOptions) => Effect.Effect<void, never, CliTheme | TerminalEnv | Audience | CliLinks>;
|
|
2444
|
+
}
|
|
2327
2445
|
declare const CliInteractive_base: Context.Reference<boolean>;
|
|
2328
2446
|
/**
|
|
2329
2447
|
* Whether this run may prompt a person: a human audience, with a terminal on
|
|
@@ -2598,6 +2716,22 @@ interface FailureDetails {
|
|
|
2598
2716
|
readonly status?: boolean | undefined;
|
|
2599
2717
|
readonly spans?: "app" | "all" | "off" | undefined;
|
|
2600
2718
|
}) => ReadonlyArray<string>;
|
|
2719
|
+
/**
|
|
2720
|
+
* The exit code this failure exits with: the error's own `Runtime.errorExitCode` when it carries one, else its
|
|
2721
|
+
* entry in `exitCodes` (a typed failure only), else `exitCode`, else `1`.
|
|
2722
|
+
*
|
|
2723
|
+
* @remarks
|
|
2724
|
+
* The code is decided before `render` is called, so a render can print it. A hook that parses a fixed stderr shape
|
|
2725
|
+
* builds it here, reading the tag off `error` itself:
|
|
2726
|
+
*
|
|
2727
|
+
* ```ts
|
|
2728
|
+
* const render = (error: unknown, details: FailureDetails) => {
|
|
2729
|
+
* const tag = Predicate.hasProperty(error, "_tag") && Predicate.isString(error._tag) ? error._tag : "Defect";
|
|
2730
|
+
* return `${details.exitCode} ${tag}: ${Fmt.sanitize(Fmt.describe(error))}`;
|
|
2731
|
+
* };
|
|
2732
|
+
* ```
|
|
2733
|
+
*/
|
|
2734
|
+
readonly exitCode: number;
|
|
2601
2735
|
}
|
|
2602
2736
|
/**
|
|
2603
2737
|
* How a failure is turned into output and an exit code.
|
|
@@ -2637,6 +2771,23 @@ interface ReportFailuresOptions {
|
|
|
2637
2771
|
* POSIX exit status can carry — `256` wraps to `0` and passes a failed run.
|
|
2638
2772
|
*/
|
|
2639
2773
|
readonly exitCode?: number | undefined;
|
|
2774
|
+
/**
|
|
2775
|
+
* Exit codes by tag: a typed failure whose `_tag` is a key here exits with that code.
|
|
2776
|
+
*
|
|
2777
|
+
* @remarks
|
|
2778
|
+
* Checked after the error's own `Runtime.errorExitCode` (an error that chose its code keeps it) and before
|
|
2779
|
+
* `exitCode`, which stays the fallback for every other failure, defects included. Only a typed failure is looked
|
|
2780
|
+
* up: a defect that happens to carry a `_tag` exits with `exitCode`. A usage error (`usageExitCode`), a cancel
|
|
2781
|
+
* (`130`) and a non-interactive prompt (`64`) carry their own codes and are not looked up either.
|
|
2782
|
+
*
|
|
2783
|
+
* Each code must be an integer in `1..255`: `0` would pass a failed run, and `256` wraps to `0`. Anything else
|
|
2784
|
+
* throws a `RangeError` when the options are passed, since it is a wiring mistake, not a runtime condition.
|
|
2785
|
+
*
|
|
2786
|
+
* ```ts
|
|
2787
|
+
* CliRuntime.main(program, { platform, exitCodes: { DataStoreError: 3, SidecarTimeoutError: 2 }, exitCode: 5 })
|
|
2788
|
+
* ```
|
|
2789
|
+
*/
|
|
2790
|
+
readonly exitCodes?: Readonly<Record<string, number>> | undefined;
|
|
2640
2791
|
/**
|
|
2641
2792
|
* The exit code for a usage error: a `ShowHelp` carrying parse errors, or a
|
|
2642
2793
|
* `CliError.UserError` `Command.runWith` already printed.
|
|
@@ -2713,6 +2864,26 @@ interface MainOptions<RP, EP> extends ReportFailuresOptions {
|
|
|
2713
2864
|
* no errors are printed, so nothing marks the help as a usage error's.
|
|
2714
2865
|
*/
|
|
2715
2866
|
readonly helpOnUsageError?: "stdout" | "stderr" | undefined;
|
|
2867
|
+
/**
|
|
2868
|
+
* The carrier package the bin was installed through, when a carrier launched it: `null` (or `undefined`) for a
|
|
2869
|
+
* direct install.
|
|
2870
|
+
*
|
|
2871
|
+
* @remarks
|
|
2872
|
+
* Given (even as `null`), `main` provides `@effected/engine`'s `CurrentDistribution` around the program, as
|
|
2873
|
+
* `Option.some` of the carrier or `Option.none()`, so a program and the engine it calls read the same value. With a
|
|
2874
|
+
* carrier, `--version` ends with its `via <name> <version>` suffix (`distributionSuffix`), appended to whatever
|
|
2875
|
+
* `formatVersion` is in effect, the coloured default or `env.formatter`'s: do not also append it in
|
|
2876
|
+
* `env.formatter`.
|
|
2877
|
+
*
|
|
2878
|
+
* Core's default `formatVersion` prints `<name> v<version>`. A program moving off a hand-rolled `--version` that
|
|
2879
|
+
* printed another shape (no `v`, say) keeps it by passing that shape as `env.formatter`'s `formatVersion`; the
|
|
2880
|
+
* suffix is appended to it either way.
|
|
2881
|
+
*
|
|
2882
|
+
* ```ts
|
|
2883
|
+
* CliRuntime.main(CliAudience.run(root, { version }), { platform, env, distribution: options.distribution ?? null })
|
|
2884
|
+
* ```
|
|
2885
|
+
*/
|
|
2886
|
+
readonly distribution?: Distribution | null | undefined;
|
|
2716
2887
|
}
|
|
2717
2888
|
/**
|
|
2718
2889
|
* Report a CLI program's failures through the program's own logger.
|
|
@@ -2988,6 +3159,20 @@ interface PercentOptions {
|
|
|
2988
3159
|
*/
|
|
2989
3160
|
readonly scale?: 1 | 100 | undefined;
|
|
2990
3161
|
}
|
|
3162
|
+
/**
|
|
3163
|
+
* Options for {@link Fmt.pad}.
|
|
3164
|
+
*
|
|
3165
|
+
* @public
|
|
3166
|
+
*/
|
|
3167
|
+
interface PadOptions {
|
|
3168
|
+
/**
|
|
3169
|
+
* Where the text sits in the padded field: `left` (the default) fills on the right, `right` fills on the left, and
|
|
3170
|
+
* `center` splits the fill, the odd column going on the right.
|
|
3171
|
+
*/
|
|
3172
|
+
readonly align?: "left" | "right" | "center" | undefined;
|
|
3173
|
+
/** The fill, one column wide; a space by default. Anything that is not exactly one column wide is a space. */
|
|
3174
|
+
readonly fill?: string | undefined;
|
|
3175
|
+
}
|
|
2991
3176
|
/**
|
|
2992
3177
|
* Small, pure formatting primitives for terminal output.
|
|
2993
3178
|
*
|
|
@@ -3067,6 +3252,86 @@ export declare class Fmt {
|
|
|
3067
3252
|
* @param plural - the noun otherwise; `singular` with an `s` appended by default
|
|
3068
3253
|
*/
|
|
3069
3254
|
static readonly plural: (n: number, singular: string, plural?: string) => string;
|
|
3255
|
+
/**
|
|
3256
|
+
* Pad `text` to `width` terminal columns, measured as {@link Fmt.width} measures: a wide character counts two and
|
|
3257
|
+
* an escape sequence none, so coloured text pads to the same column as plain text.
|
|
3258
|
+
*
|
|
3259
|
+
* @remarks
|
|
3260
|
+
* Text already `width` columns or wider is returned unchanged: compose with {@link Fmt.truncate} to cut it. A
|
|
3261
|
+
* `width` that is not a finite number pads nothing. Never throws.
|
|
3262
|
+
*
|
|
3263
|
+
* @param text - the text to pad
|
|
3264
|
+
* @param width - the columns the result should take
|
|
3265
|
+
* @param options - the alignment and the fill
|
|
3266
|
+
*/
|
|
3267
|
+
static readonly pad: (text: string, width: number, options?: PadOptions) => string;
|
|
3268
|
+
/**
|
|
3269
|
+
* `text` without its escape sequences: CSI (colour, cursor moves, erases), OSC (titles, OSC 8 hyperlinks) ending in
|
|
3270
|
+
* BEL or ST, the DCS, SOS, PM and APC strings, and every other escape, the nF sequences (`ESC ( B`) and the
|
|
3271
|
+
* two-byte ones (`ESC 7`, `ESC 8`, `ESC =`, `ESC c`).
|
|
3272
|
+
*
|
|
3273
|
+
* @remarks
|
|
3274
|
+
* The same strip {@link Fmt.width} and {@link Fmt.sanitize} start from. Only complete sequences go: a lone ESC, or
|
|
3275
|
+
* any other control character, is kept, and so is an ESC in front of a lowercase letter other than `c`, which a
|
|
3276
|
+
* stray ESC before a word would otherwise cost the word its first letter. An unterminated CSI or OSC keeps its `[`
|
|
3277
|
+
* or `]`. Use {@link Fmt.sanitize} for text that will be laid out and printed.
|
|
3278
|
+
*
|
|
3279
|
+
* @param text - the text
|
|
3280
|
+
*/
|
|
3281
|
+
static readonly stripAnsi: (text: string) => string;
|
|
3282
|
+
/**
|
|
3283
|
+
* Whether `text` is nothing but escape sequences, as {@link Fmt.stripAnsi} reads them: a cursor hide or show, a
|
|
3284
|
+
* clear-screen, an erase-scrollback. Empty text is not escape-only, and neither is a bare control character
|
|
3285
|
+
* (`\r`, BEL), which is not an escape sequence.
|
|
3286
|
+
*
|
|
3287
|
+
* @param text - the text
|
|
3288
|
+
*/
|
|
3289
|
+
static readonly isEscapeOnly: (text: string) => boolean;
|
|
3290
|
+
/**
|
|
3291
|
+
* Integers as compact ranges: `[1, 2, 3, 5, 10, 11]` is `"1-3,5,10-11"`, a run of consecutive numbers written
|
|
3292
|
+
* `start-end` and the rest one by one, joined with commas.
|
|
3293
|
+
*
|
|
3294
|
+
* @remarks
|
|
3295
|
+
* The input is sorted and its duplicates dropped first, so order does not matter. Anything that is not a finite
|
|
3296
|
+
* integer is left out, and no input is `""`. Never throws.
|
|
3297
|
+
*
|
|
3298
|
+
* @param numbers - the integers, such as line numbers
|
|
3299
|
+
*/
|
|
3300
|
+
static readonly ranges: (numbers: Iterable<number>) => string;
|
|
3301
|
+
/**
|
|
3302
|
+
* Any value as text, without ever throwing.
|
|
3303
|
+
*
|
|
3304
|
+
* @remarks
|
|
3305
|
+
* A string is itself; a number, boolean, bigint or symbol is `String(value)`; `undefined` and `null` are
|
|
3306
|
+
* `"undefined"` and `"null"`. A value with a string `message` (an `Error`, one from another realm, a plain
|
|
3307
|
+
* `{ message }`) is that message. Anything else is its JSON, or `String(value)` when it has none (a cycle, a
|
|
3308
|
+
* function), or `"[unprintable value]"` when that throws too. A getter that throws is a value with no message.
|
|
3309
|
+
*
|
|
3310
|
+
* The text is not sanitised: pass it through {@link Fmt.sanitize} before printing data you do not control. For
|
|
3311
|
+
* the messages along an error's `cause` chain, use {@link Fmt.causeMessage}.
|
|
3312
|
+
*
|
|
3313
|
+
* @param value - the value
|
|
3314
|
+
*/
|
|
3315
|
+
static readonly describe: (value: unknown) => string;
|
|
3316
|
+
/**
|
|
3317
|
+
* Every distinct non-empty message along a value's `cause` chain, nearest first and joined with `: `, or
|
|
3318
|
+
* `undefined` when nothing along it has one.
|
|
3319
|
+
*
|
|
3320
|
+
* @remarks
|
|
3321
|
+
* The kit's one guarded cause-message read, the one its own errors render a preserved cause with. A node's message
|
|
3322
|
+
* is a thrown string or a non-empty string `message` property, read without `instanceof Error`, so an error from
|
|
3323
|
+
* another realm or a plain `{ message }` object counts. A deeper message the joined text already contains is not
|
|
3324
|
+
* repeated, and one that contains the joined text replaces it, so a `SqlError` wrapping SQLite's own text reads
|
|
3325
|
+
* `Failed to execute statement: SQLITE_BUSY: database is locked`. It is the whole chain, not the innermost
|
|
3326
|
+
* message alone. The walk stops after eight nodes, so a cyclic chain terminates.
|
|
3327
|
+
*
|
|
3328
|
+
* Never throws: a `message` getter that throws leaves that node without text, and a `cause` getter that throws ends
|
|
3329
|
+
* the walk with the text found so far, `undefined` when there is none, so a caller's own fallback
|
|
3330
|
+
* (`?? Fmt.describe(value)`) applies.
|
|
3331
|
+
*
|
|
3332
|
+
* @param value - the error, or whatever was thrown
|
|
3333
|
+
*/
|
|
3334
|
+
static readonly causeMessage: (value: unknown) => string | undefined;
|
|
3070
3335
|
}
|
|
3071
3336
|
/**
|
|
3072
3337
|
* The severity of a GitHub annotation.
|
|
@@ -3317,6 +3582,8 @@ export declare class Render {
|
|
|
3317
3582
|
* - `groupDepth` is the fiber's count of open Actions log groups (the fiber-local keyed by
|
|
3318
3583
|
* `WorkflowCommand.groupDepthKey`, which `ActionLogger.group` provides), set when it is above `0`, so a
|
|
3319
3584
|
* document printed inside a group opens no second one; see {@link RenderContext.groupDepth}.
|
|
3585
|
+
* - `displayPath` is the option, else the run's own path display (`CliEnvOptions.displayPath`, which
|
|
3586
|
+
* `CliEnv.layer` sets), else the identity.
|
|
3320
3587
|
*
|
|
3321
3588
|
* @param stream - the stream the output is for
|
|
3322
3589
|
* @param options - an explicit width and a path display function
|
|
@@ -3527,5 +3794,5 @@ export declare class SchemaIssueRenderer {
|
|
|
3527
3794
|
*/
|
|
3528
3795
|
static readonly render: (issue: unknown) => ReadonlyArray<string>;
|
|
3529
3796
|
}
|
|
3530
|
-
export type { AnnotationLevel, AnnotationOptions, AudienceFlagInput, Block, BlockOf, CliAudienceFlagsOptions, CliDocSource, CliEnvOptions, CliEnvServices, CliEnvTestOptions, CliEnvTestServices, CliExitShape, CliFailureOptions, CliLinksLinkerOptions, CliLinksOptions, CliLinksShape, CliLogFile, CliLogFileOptions, CliLogOptions, CliLogStatusOptions, CliLoggerOptions, CliMessageOptions, CliPromptFallbackOptions, CliPromptTarget, CliThemeOptions, CliThemeShape, CliThemeTestOptions, Column, CoreStatusName, Counter, CountsOptions, CountsRow, CountsTableOptions, DocPrintOptions, Document, EditorLinks, FailureDetails, GithubAnnotationProperties, GlyphSelectOptions, GlyphSet, Inline, InlineInput, InlineOf, LinkOptions, LinkTarget, ListOptions, MainOptions, NamedColor, OverflowOptions, PercentOptions, RenderContext, RenderContextOfOptions, RenderContextOptions, ReportFailuresOptions, RequiresAudienceFlags, StatusDef, StatusRef, StreamTheme, Style, TableOptions, TokenName, TreeInput, TreeNode, TruncateOptions };
|
|
3797
|
+
export type { AnnotationLevel, AnnotationOptions, AudienceFlagInput, Block, BlockOf, CliAudienceFlagsOptions, CliDocSource, CliEnvOptions, CliEnvServices, CliEnvTestOptions, CliEnvTestServices, CliExitShape, CliFailureOptions, CliFormatFlagOptions, CliFormatName, CliFormatOutput, CliFormatPrintOptions, CliLinksLinkerOptions, CliLinksOptions, CliLinksShape, CliLogFile, CliLogFileOptions, CliLogOptions, CliLogStatusOptions, CliLoggerOptions, CliMessageOptions, CliPromptFallbackOptions, CliPromptTarget, CliThemeOptions, CliThemeShape, CliThemeTestOptions, Column, CoreStatusName, Counter, CountsOptions, CountsRow, CountsTableOptions, DocPrintOptions, Document, EditorLinks, FailureDetails, GithubAnnotationProperties, GlyphSelectOptions, GlyphSet, Inline, InlineInput, InlineOf, LinkOptions, LinkTarget, ListOptions, MainOptions, NamedColor, OverflowOptions, PadOptions, PercentOptions, RenderContext, RenderContextOfOptions, RenderContextOptions, ReportFailuresOptions, RequiresAudienceFlags, StatusDef, StatusRef, StreamTheme, Style, TableOptions, TokenName, TreeInput, TreeNode, TruncateOptions };
|
|
3531
3798
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { CliColor } from "./CliColor.js";
|
|
|
15
15
|
import { CliPrompt } from "./CliPrompt.js";
|
|
16
16
|
import { CliEnv } from "./CliEnv.js";
|
|
17
17
|
import { CliExit } from "./CliExit.js";
|
|
18
|
+
import { CliFormat } from "./CliFormat.js";
|
|
18
19
|
import { CliLogger } from "./CliLogger.js";
|
|
19
20
|
import { CliLog } from "./CliLog.js";
|
|
20
21
|
import { CliMessage } from "./CliMessage.js";
|
|
@@ -23,4 +24,4 @@ import { ConfigIssueRenderer } from "./ConfigIssueRenderer.js";
|
|
|
23
24
|
import { GithubAnnotation } from "./GithubAnnotation.js";
|
|
24
25
|
import { SchemaIssueRenderer } from "./SchemaIssueRenderer.js";
|
|
25
26
|
|
|
26
|
-
export { Cancelled, CliAudience, CliColor, CliDoc, CliEnv, CliExit, CliFailure, CliInteractive, CliLinks, CliLog, CliLogger, CliMessage, CliPrompt, CliRuntime, CliTheme, ConfigIssueRenderer, Doc, Fmt, GithubAnnotation, Glyphs, NotInteractive, Render, SchemaIssueRenderer, Status, Token };
|
|
27
|
+
export { Cancelled, CliAudience, CliColor, CliDoc, CliEnv, CliExit, CliFailure, CliFormat, CliInteractive, CliLinks, CliLog, CliLogger, CliMessage, CliPrompt, CliRuntime, CliTheme, ConfigIssueRenderer, Doc, Fmt, GithubAnnotation, Glyphs, NotInteractive, Render, SchemaIssueRenderer, Status, Token };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Predicate } from "effect";
|
|
2
|
+
|
|
3
|
+
// src/internal/causeMessage.ts
|
|
4
|
+
/** How many nodes of a cause chain the walk reads before stopping, so a cyclic chain cannot spin. */
|
|
5
|
+
const CAUSE_DEPTH = 8;
|
|
6
|
+
/**
|
|
7
|
+
* The message a single node carries: a thrown string, or a non-empty string `message` property. A getter or
|
|
8
|
+
* proxy trap that throws is a node with no message.
|
|
9
|
+
*/
|
|
10
|
+
const messageAt = (node) => {
|
|
11
|
+
try {
|
|
12
|
+
const text = Predicate.isString(node) ? node : Predicate.hasProperty(node, "message") && Predicate.isString(node.message) ? node.message : void 0;
|
|
13
|
+
return text === void 0 || text.trim() === "" ? void 0 : text;
|
|
14
|
+
} catch {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
/** The next link of the chain: a node's `cause`, or `undefined` when it has none or reading it throws. */
|
|
19
|
+
const causeAt = (node) => {
|
|
20
|
+
try {
|
|
21
|
+
return Predicate.hasProperty(node, "cause") ? node.cause : void 0;
|
|
22
|
+
} catch {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Every distinct non-empty message a preserved cause chain carries, nearest
|
|
28
|
+
* first and joined with `: `, or `undefined` when nothing along it has one.
|
|
29
|
+
*
|
|
30
|
+
* A `Schema.Defect()` field holds whatever was thrown, so the read is guarded
|
|
31
|
+
* rather than assumed: `Predicate.hasProperty` + `Predicate.isString` accepts
|
|
32
|
+
* an `Error`, a cross-realm `Error` that fails `instanceof`, a plain
|
|
33
|
+
* `{ message }` object and a thrown string alike. An empty or whitespace-only
|
|
34
|
+
* message counts as none. The walk follows `cause` links because a wrapper's
|
|
35
|
+
* text is often the generic summary and the driver's text underneath is the
|
|
36
|
+
* actionable detail, yet a wrapper that composes its own context must not
|
|
37
|
+
* lose it either; so nothing is dropped, and a deeper text the nearer one
|
|
38
|
+
* already contains is not repeated. The walk is bounded, so a cycle terminates.
|
|
39
|
+
*
|
|
40
|
+
* It never throws: every property read is guarded, so a `message` getter that throws leaves that link
|
|
41
|
+
* without text, and a `cause` getter that throws ends the walk with what was found so far.
|
|
42
|
+
*/
|
|
43
|
+
const causeMessage = (cause) => {
|
|
44
|
+
let message;
|
|
45
|
+
let node = cause;
|
|
46
|
+
for (let depth = 0; depth < CAUSE_DEPTH && node !== void 0 && node !== null; depth++) {
|
|
47
|
+
const text = messageAt(node);
|
|
48
|
+
if (text !== void 0) {
|
|
49
|
+
if (message === void 0 || text.includes(message)) message = text;
|
|
50
|
+
else if (!message.includes(text)) message = `${message}: ${text}`;
|
|
51
|
+
}
|
|
52
|
+
node = causeAt(node);
|
|
53
|
+
}
|
|
54
|
+
return message;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export { causeMessage };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Option, Path } from "effect";
|
|
2
|
+
|
|
3
|
+
// src/internal/displayPath.ts
|
|
4
|
+
/**
|
|
5
|
+
* The display that shows every path as it is.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
const identity = (absolute) => absolute;
|
|
10
|
+
/**
|
|
11
|
+
* The path display in effect: the identity unless `CliEnv.layer` was given a `displayPath`.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
const CurrentDisplayPath = Context.Reference("@effected/cli/CurrentDisplayPath", { defaultValue: () => identity });
|
|
16
|
+
/**
|
|
17
|
+
* A path relative to `root` when it lies under it, and the path unchanged when it is `root` itself or lies outside it
|
|
18
|
+
* (a `../..` chain says less than the absolute path). With a `Path` service its `relative` decides; without one, a
|
|
19
|
+
* plain prefix test on `/` and `\` does.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
const relativeTo = (root, path) => Option.match(path, {
|
|
24
|
+
onSome: (p) => (absolute) => {
|
|
25
|
+
const rel = p.relative(root, absolute);
|
|
26
|
+
return rel === "" || rel === ".." || rel.startsWith(`..${p.sep}`) || rel.startsWith("../") || p.isAbsolute(rel) ? absolute : rel;
|
|
27
|
+
},
|
|
28
|
+
onNone: () => {
|
|
29
|
+
let end = root.length;
|
|
30
|
+
while (end > 0 && (root[end - 1] === "/" || root[end - 1] === "\\")) end -= 1;
|
|
31
|
+
const base = root.slice(0, end);
|
|
32
|
+
return (absolute) => {
|
|
33
|
+
const next = absolute.charAt(base.length);
|
|
34
|
+
return absolute.startsWith(base) && (next === "/" || next === "\\") && absolute.length > base.length + 1 ? absolute.slice(base.length + 1) : absolute;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Sets {@link CurrentDisplayPath} from the option, reading `Path` from the context when it is there. No option sets
|
|
40
|
+
* nothing.
|
|
41
|
+
*
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
const displayPathLayer = (option) => option === void 0 ? Layer.empty : Layer.effect(CurrentDisplayPath)(typeof option === "function" ? Effect.succeed(option) : Effect.map(Effect.serviceOption(Path.Path), (path) => relativeTo(option.relativeTo, path)));
|
|
45
|
+
|
|
46
|
+
export { CurrentDisplayPath, displayPathLayer, identity, relativeTo };
|
package/internal/displayWidth.js
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
// src/internal/displayWidth.ts
|
|
2
2
|
const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
3
|
-
const ANSI = /\u001B
|
|
3
|
+
const ANSI = /\u001B(?:\[[0-?]*[ -/]*[@-~]|\][^\u0007\u001B]*(?:\u0007|\u001B\\)|[PX^_][^\u001B]*\u001B\\|[()*+\-./][0-~]|\$[()*+\-./]?[@-~]|#[3-68]| [FGLMN]|[0-?@-Z\\^_c])/g;
|
|
4
4
|
const EMOJI = /^\p{RGI_Emoji}$/v;
|
|
5
5
|
const WIDE2 = /[\p{Emoji_Presentation}\u1100-\u115E\u2329\u232A\u2630-\u2637\u268A-\u268F\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312F\u3131-\u3163\u3165-\u318E\u3190-\u31E5\u31EF-\u321E\u3220-\u3247\u3250-\u4DBF\u4DC0-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6\u{16FE0}-\u{16FF6}\u{17000}-\u{191FF}\u{1AFF0}-\u{1AFFF}\u{1B000}-\u{1B2FF}\u{1D300}-\u{1D376}\u{1F200}-\u{1F265}\u{20000}-\u{3FFFD}]/u;
|
|
6
6
|
const ZERO2 = /^[\p{Mn}\p{Me}\p{Cf}\p{Cc}\u115F\u1160\u3164\uFFA0]+$/u;
|
|
7
7
|
/**
|
|
8
|
-
* The text without its
|
|
8
|
+
* The text without its escape sequences: CSI (including SGR colour), OSC (including OSC-8 hyperlinks), the DCS, SOS,
|
|
9
|
+
* PM and APC strings, and every other escape (`ESC c`, `ESC 7`, `ESC ( B`).
|
|
9
10
|
*
|
|
10
11
|
* @internal
|
|
11
12
|
*/
|
|
12
13
|
const stripAnsi = (input) => input.replace(ANSI, "");
|
|
13
14
|
/**
|
|
15
|
+
* Whether `input` is nothing but escape sequences, as {@link stripAnsi} reads them; empty text is not. Every sequence
|
|
16
|
+
* starts with ESC, so text that does not is answered without running the strip.
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
const isEscapeOnly = (input) => input.charCodeAt(0) === 27 && stripAnsi(input) === "";
|
|
21
|
+
/**
|
|
14
22
|
* The display width of `input` in terminal columns: graphemes, wide East Asian characters and emoji count two,
|
|
15
23
|
* combining marks, control characters and ANSI escapes count none.
|
|
16
24
|
*
|
|
@@ -31,4 +39,4 @@ const displayWidth = (input) => {
|
|
|
31
39
|
*/
|
|
32
40
|
const graphemes = (input) => Array.from(segmenter.segment(input), (s) => s.segment);
|
|
33
41
|
|
|
34
|
-
export { displayWidth, graphemes, stripAnsi };
|
|
42
|
+
export { displayWidth, graphemes, isEscapeOnly, stripAnsi };
|
|
@@ -3,6 +3,7 @@ import { sanitize } from "../Fmt.js";
|
|
|
3
3
|
import { CliLinks } from "../CliLinks.js";
|
|
4
4
|
import { Glyphs } from "../Glyphs.js";
|
|
5
5
|
import { CliTheme } from "../CliTheme.js";
|
|
6
|
+
import { identity } from "./displayPath.js";
|
|
6
7
|
import { Render } from "../Render.js";
|
|
7
8
|
import { CliFailure } from "../CliFailure.js";
|
|
8
9
|
import { Config, Context, Effect, MutableRef, Option } from "effect";
|
|
@@ -31,7 +32,7 @@ const fallbackTarget = {
|
|
|
31
32
|
paint: (_token, text) => text,
|
|
32
33
|
glyphs: Glyphs.ascii,
|
|
33
34
|
link: (_target, label) => label,
|
|
34
|
-
displayPath:
|
|
35
|
+
displayPath: identity,
|
|
35
36
|
neutralizeWorkflowCommands: true
|
|
36
37
|
},
|
|
37
38
|
format: "plain",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/internal/inspect.ts
|
|
2
|
+
/** The value's `_tag` when it is a string; `undefined` otherwise, and when reading it throws. */
|
|
3
|
+
const tagOf = (value) => {
|
|
4
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5
|
+
try {
|
|
6
|
+
const tag = value._tag;
|
|
7
|
+
return typeof tag === "string" ? tag : void 0;
|
|
8
|
+
} catch {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* A failure value as report text: `String(value)`, except that a plain object whose `String` says only
|
|
14
|
+
* `[object Object]` is its string `message` when it has one. `String(value)` throws for an object with no prototype or
|
|
15
|
+
* a hostile `toString`: that value is `unprintable`.
|
|
16
|
+
*/
|
|
17
|
+
const failureText = (value, unprintable = "[unprintable value]") => {
|
|
18
|
+
try {
|
|
19
|
+
const text = String(value);
|
|
20
|
+
if (text !== "[object Object]") return text;
|
|
21
|
+
const message = value?.message;
|
|
22
|
+
return typeof message === "string" ? message : text;
|
|
23
|
+
} catch {
|
|
24
|
+
return unprintable;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* A property of a failure value, or `undefined` when it has none or reading it throws: a report must never fail on a
|
|
29
|
+
* hostile getter. An `Error`'s `stack` counts, since V8 formats it on first read and the formatting reads `message`.
|
|
30
|
+
*/
|
|
31
|
+
const readProperty = (value, key) => {
|
|
32
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
33
|
+
try {
|
|
34
|
+
return value[key];
|
|
35
|
+
} catch {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export { failureText, readProperty, tagOf };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The presentation boundary of an effect/cli program: audience-aware output, a document IR and renderers, editor links, failure reports and logging, plus opt-in Ink screens, widgets and a live view",
|
|
6
6
|
"keywords": [
|
|
@@ -57,13 +57,15 @@
|
|
|
57
57
|
"./package.json": "./package.json"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
+
"@effected/commands": "^0.17.0",
|
|
61
|
+
"@effected/engine": "^0.9.0",
|
|
60
62
|
"@effected/github-commands": "^0.6.2"
|
|
61
63
|
},
|
|
62
64
|
"peerDependencies": {
|
|
63
|
-
"@effected/config-file": "^0.
|
|
64
|
-
"@effected/env": "^0.
|
|
65
|
-
"@effected/glob": "^0.
|
|
66
|
-
"@effected/walker": "^0.
|
|
65
|
+
"@effected/config-file": "^0.21.0",
|
|
66
|
+
"@effected/env": "^0.6.0",
|
|
67
|
+
"@effected/glob": "^0.15.0",
|
|
68
|
+
"@effected/walker": "^0.21.0",
|
|
67
69
|
"@types/react": "^19.3.0",
|
|
68
70
|
"effect": "^4.0.0",
|
|
69
71
|
"ink": "^8.0.0",
|