@doist/cli-core 0.2.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.4.0](https://github.com/Doist/cli-core/compare/v0.3.0...v0.4.0) (2026-05-08)
2
+
3
+ ### Features
4
+
5
+ * add printEmpty + describeEmptyMachineOutput helpers ([#6](https://github.com/Doist/cli-core/issues/6)) ([2c0a74e](https://github.com/Doist/cli-core/commit/2c0a74e7874ef47184a071f9fc15f22f254ca20a))
6
+
7
+ ## [0.3.0](https://github.com/Doist/cli-core/compare/v0.2.0...v0.3.0) (2026-05-06)
8
+
9
+ ### Features
10
+
11
+ * add createSpinner factory ([#5](https://github.com/Doist/cli-core/issues/5)) ([7092427](https://github.com/Doist/cli-core/commit/70924271385bb9ad2009a0d56df5a8768c9943f7))
12
+
1
13
  ## [0.2.0](https://github.com/Doist/cli-core/compare/v0.1.0...v0.2.0) (2026-05-06)
2
14
 
3
15
  ### Features
package/dist/config.d.ts CHANGED
@@ -65,13 +65,13 @@ export type ConfigErrorCode = (typeof BROKEN_CONFIG_STATE_TO_CODE)[keyof typeof
65
65
  * decode in the consumer.
66
66
  */
67
67
  export declare function readConfigStrict(path: string): Promise<ReadConfigStrictResult>;
68
- export interface WriteConfigOptions {
68
+ export type WriteConfigOptions = {
69
69
  /**
70
70
  * When true and the supplied config has no own enumerable keys, delete the
71
71
  * file instead of writing an empty `{}`. Default false.
72
72
  */
73
73
  deleteWhenEmpty?: boolean;
74
- }
74
+ };
75
75
  /**
76
76
  * Write a config file with restrictive permissions (parent dir 0700, file 0600)
77
77
  * and a trailing newline. Creates parent directories as needed and tightens
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAGpF;AAED,MAAM,MAAM,sBAAsB,GAC5B;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,GACpB;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GACtC;IAAE,KAAK,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GACvC;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;CAAE,GACtF;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAA;AAU3D;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B;;;;CAIc,CAAA;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GACvB,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,OAAO,2BAA2B,CAAC,CAAA;AAElF;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAqBpF;AAED,MAAM,WAAW,kBAAkB;IAC/B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,CAAC,SAAS,MAAM,EAC9C,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,EACT,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC/C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,IAAI,CAAC,CAoBf"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAGpF;AAED,MAAM,MAAM,sBAAsB,GAC5B;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,GACpB;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GACtC;IAAE,KAAK,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GACvC;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;CAAE,GACtF;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAA;AAU3D;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B;;;;CAIc,CAAA;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GACvB,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,OAAO,2BAA2B,CAAC,CAAA;AAElF;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAqBpF;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC7B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;CAC5B,CAAA;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,CAAC,SAAS,MAAM,EAC9C,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,EACT,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC/C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,IAAI,CAAC,CAoBf"}
@@ -0,0 +1,15 @@
1
+ import type { ViewOptions } from './options.js';
2
+ /**
3
+ * Gate the empty-state print on the active output mode:
4
+ * --json → prints exactly `'[]'`
5
+ * --ndjson → prints nothing (no stray newline; ndjson EOF = end of stream)
6
+ * neither → prints the human-readable message
7
+ *
8
+ * Use at every list/array empty-result branch so machine consumers never see
9
+ * human strings on stdout when they asked for `--json` / `--ndjson`.
10
+ */
11
+ export declare function printEmpty({ options, message }: {
12
+ options: ViewOptions;
13
+ message: string;
14
+ }): void;
15
+ //# sourceMappingURL=empty.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty.d.ts","sourceRoot":"","sources":["../src/empty.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAShG"}
package/dist/empty.js ADDED
@@ -0,0 +1,21 @@
1
+ import { formatJson } from './json.js';
2
+ /**
3
+ * Gate the empty-state print on the active output mode:
4
+ * --json → prints exactly `'[]'`
5
+ * --ndjson → prints nothing (no stray newline; ndjson EOF = end of stream)
6
+ * neither → prints the human-readable message
7
+ *
8
+ * Use at every list/array empty-result branch so machine consumers never see
9
+ * human strings on stdout when they asked for `--json` / `--ndjson`.
10
+ */
11
+ export function printEmpty({ options, message }) {
12
+ if (options.json) {
13
+ console.log(formatJson([]));
14
+ return;
15
+ }
16
+ if (options.ndjson) {
17
+ return;
18
+ }
19
+ console.log(message);
20
+ }
21
+ //# sourceMappingURL=empty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty.js","sourceRoot":"","sources":["../src/empty.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAGtC;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,EAA6C;IACtF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAA;QAC3B,OAAM;IACV,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACjB,OAAM;IACV,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;AACxB,CAAC"}
package/dist/errors.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import type { ConfigErrorCode } from './config.js';
2
2
  export type ErrorType = 'error' | 'info';
3
- export interface CliErrorOptions {
3
+ export type CliErrorOptions = {
4
4
  hints?: string[];
5
5
  type?: ErrorType;
6
- }
6
+ };
7
7
  /**
8
8
  * Aggregator of every error code that cli-core itself defines. Baked into the
9
9
  * `CliError` constructor so consumers don't have to redeclare these strings in
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,CAAA;AAExC,MAAM,WAAW,eAAe;IAC5B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,IAAI,CAAC,EAAE,SAAS,CAAA;CACnB;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,CAAA;AAE1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,QAAQ,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,KAAK;IAC9D,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,YAAY,CAAA;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;gBAEZ,IAAI,EAAE,KAAK,GAAG,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;CAOzF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,CAAA;AAExC,MAAM,MAAM,eAAe,GAAG;IAC1B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,IAAI,CAAC,EAAE,SAAS,CAAA;CACnB,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,CAAA;AAE1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,QAAQ,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,KAAK;IAC9D,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,YAAY,CAAA;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;gBAEZ,IAAI,EAAE,KAAK,GAAG,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;CAOzF"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  export { BROKEN_CONFIG_STATE_TO_CODE, getConfigPath, readConfig, readConfigStrict, updateConfig, writeConfig, } from './config.js';
2
2
  export type { ConfigErrorCode, ReadConfigStrictResult, WriteConfigOptions } from './config.js';
3
+ export { printEmpty } from './empty.js';
3
4
  export { CliError } from './errors.js';
4
5
  export type { CliErrorCode, CliErrorOptions, ErrorType } from './errors.js';
5
6
  export { formatJson, formatNdjson } from './json.js';
7
+ export type { ViewOptions } from './options.js';
8
+ export { createSpinner } from './spinner.js';
9
+ export type { LoadingSpinner, SpinnerColor, SpinnerConfig, SpinnerKit, SpinnerOptions, } from './spinner.js';
6
10
  export { isCI, isStderrTTY, isStdinTTY, isStdoutTTY } from './terminal.js';
7
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,2BAA2B,EAC3B,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,WAAW,GACd,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,eAAe,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAC9F,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC3E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACpD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,2BAA2B,EAC3B,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,WAAW,GACd,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,eAAe,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAC9F,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC3E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,YAAY,EACR,cAAc,EACd,YAAY,EACZ,aAAa,EACb,UAAU,EACV,cAAc,GACjB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  export { BROKEN_CONFIG_STATE_TO_CODE, getConfigPath, readConfig, readConfigStrict, updateConfig, writeConfig, } from './config.js';
2
+ export { printEmpty } from './empty.js';
2
3
  export { CliError } from './errors.js';
3
4
  export { formatJson, formatNdjson } from './json.js';
5
+ export { createSpinner } from './spinner.js';
4
6
  export { isCI, isStderrTTY, isStdinTTY, isStdoutTTY } from './terminal.js';
5
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,2BAA2B,EAC3B,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,WAAW,GACd,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACpD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,2BAA2B,EAC3B,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,WAAW,GACd,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAQ5C,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Shared shape for commands that respect the canonical machine-output flags.
3
+ * Seeded narrow so the type only declares what cli-core helpers actually read
4
+ * today; will grow (`full?`, `raw?`, etc.) as the global-args parser extraction
5
+ * lands (see EXTRACTION_ROADMAP.md, Tier 1).
6
+ *
7
+ * Per-CLI `ViewOptions` types should extend this rather than re-declare the
8
+ * `json` / `ndjson` fields.
9
+ */
10
+ export type ViewOptions = {
11
+ json?: boolean;
12
+ ndjson?: boolean;
13
+ };
14
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG;IACtB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":""}
@@ -0,0 +1,60 @@
1
+ export type SpinnerColor = 'green' | 'yellow' | 'blue' | 'red' | 'gray' | 'cyan' | 'magenta';
2
+ export type SpinnerOptions = {
3
+ text: string;
4
+ color?: SpinnerColor;
5
+ /** Per-call opt-out (e.g. when a flag like `--no-spinner` was passed). */
6
+ noSpinner?: boolean;
7
+ };
8
+ export type SpinnerConfig = {
9
+ /**
10
+ * Returns true to suppress every spinner produced by this kit. CLIs
11
+ * supply this to combine well-known signals (CI, `--json`, etc.) with
12
+ * their own opt-out env vars (e.g. `TD_SPINNER=false`).
13
+ *
14
+ * Defaults to `() => false`.
15
+ */
16
+ isDisabled?: () => boolean;
17
+ /** Default colour name. Defaults to `'blue'`. */
18
+ defaultColor?: SpinnerColor;
19
+ /** Default text shown by `startEarlySpinner` when no override is passed. */
20
+ earlySpinnerText?: string;
21
+ };
22
+ export type LoadingSpinner = {
23
+ start(options: SpinnerOptions): LoadingSpinner;
24
+ succeed(text?: string): void;
25
+ fail(text?: string): void;
26
+ stop(): void;
27
+ };
28
+ export type SpinnerKit = {
29
+ /** Class so consumers can use `new spinner.LoadingSpinner()` if they want
30
+ * to manage start/stop manually. Most call sites should prefer `withSpinner`. */
31
+ LoadingSpinner: new () => LoadingSpinner;
32
+ /** Run an async operation flanked by a spinner. Stops on success, fails on throw. */
33
+ withSpinner: <T>(options: SpinnerOptions, op: () => Promise<T>) => Promise<T>;
34
+ /** Show a long-running spinner before the command module loads. Intercepts
35
+ * stdout.write so it auto-clears the moment any output is produced. */
36
+ startEarlySpinner: (text?: string) => void;
37
+ /** Stop the early spinner if running and restore stdout.write. */
38
+ stopEarlySpinner: () => void;
39
+ /**
40
+ * @internal Reset internal state without calling `.stop()`. Exposed only
41
+ * so consumer test suites can fully reset the kit between cases; do not
42
+ * call from production code.
43
+ */
44
+ resetEarlySpinner: () => void;
45
+ };
46
+ /**
47
+ * Build a spinner kit configured for one CLI. Each kit owns its own early-
48
+ * spinner singleton state, so two kits running in the same process don't
49
+ * step on each other.
50
+ *
51
+ * ```ts
52
+ * import { createSpinner } from '@doist/cli-core'
53
+ * import { shouldDisableSpinner } from './global-args.js'
54
+ *
55
+ * const { LoadingSpinner, withSpinner, startEarlySpinner, stopEarlySpinner } =
56
+ * createSpinner({ isDisabled: shouldDisableSpinner })
57
+ * ```
58
+ */
59
+ export declare function createSpinner(config?: SpinnerConfig): SpinnerKit;
60
+ //# sourceMappingURL=spinner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../src/spinner.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;AAE5F,MAAM,MAAM,cAAc,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IACxB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,OAAO,CAAA;IAC1B,iDAAiD;IACjD,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IACzB,KAAK,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CAAA;IAC9C,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,IAAI,IAAI,CAAA;CACf,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACrB;sFACkF;IAClF,cAAc,EAAE,UAAU,cAAc,CAAA;IACxC,qFAAqF;IACrF,WAAW,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7E;4EACwE;IACxE,iBAAiB,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,kEAAkE;IAClE,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,IAAI,CAAA;CAChC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,MAAM,GAAE,aAAkB,GAAG,UAAU,CAiJpE"}
@@ -0,0 +1,151 @@
1
+ import chalk from 'chalk';
2
+ import yoctoSpinner from 'yocto-spinner';
3
+ import { isStdoutTTY } from './terminal.js';
4
+ /**
5
+ * Build a spinner kit configured for one CLI. Each kit owns its own early-
6
+ * spinner singleton state, so two kits running in the same process don't
7
+ * step on each other.
8
+ *
9
+ * ```ts
10
+ * import { createSpinner } from '@doist/cli-core'
11
+ * import { shouldDisableSpinner } from './global-args.js'
12
+ *
13
+ * const { LoadingSpinner, withSpinner, startEarlySpinner, stopEarlySpinner } =
14
+ * createSpinner({ isDisabled: shouldDisableSpinner })
15
+ * ```
16
+ */
17
+ export function createSpinner(config = {}) {
18
+ const isDisabled = config.isDisabled ?? (() => false);
19
+ const defaultColor = config.defaultColor ?? 'blue';
20
+ const earlyText = config.earlySpinnerText ?? 'Loading...';
21
+ let earlyInstance = null;
22
+ let originalStdoutWrite = null;
23
+ let stdoutInterceptor = null;
24
+ function restoreStdoutWrite() {
25
+ if (!originalStdoutWrite)
26
+ return;
27
+ // Only restore if our interceptor is still on top — anything else that
28
+ // monkey-patched process.stdout.write after us should keep its hook.
29
+ if (process.stdout.write === stdoutInterceptor) {
30
+ process.stdout.write = originalStdoutWrite;
31
+ }
32
+ originalStdoutWrite = null;
33
+ stdoutInterceptor = null;
34
+ }
35
+ function startEarlySpinner(text = earlyText) {
36
+ if (!isStdoutTTY() || isDisabled())
37
+ return;
38
+ earlyInstance = yoctoSpinner({ text: chalk[defaultColor](text) });
39
+ earlyInstance.start();
40
+ // Capture the original in a local closure variable (no .bind()) so
41
+ // repeat startEarlySpinner calls don't nest .bind() copies, and so
42
+ // the wrapper's reference survives `stopEarlySpinner` clearing the
43
+ // module-level `originalStdoutWrite` field.
44
+ const savedWrite = process.stdout.write;
45
+ originalStdoutWrite = savedWrite;
46
+ const wrapper = function (...args) {
47
+ stopEarlySpinner();
48
+ return savedWrite.apply(this, args);
49
+ };
50
+ stdoutInterceptor = wrapper;
51
+ process.stdout.write = wrapper;
52
+ }
53
+ function stopEarlySpinner() {
54
+ restoreStdoutWrite();
55
+ if (earlyInstance) {
56
+ earlyInstance.stop();
57
+ earlyInstance = null;
58
+ }
59
+ }
60
+ function resetEarlySpinner() {
61
+ restoreStdoutWrite();
62
+ earlyInstance = null;
63
+ }
64
+ class LoadingSpinnerImpl {
65
+ instance = null;
66
+ adopted = false;
67
+ start(options) {
68
+ if (!isStdoutTTY() || options.noSpinner || isDisabled()) {
69
+ return this;
70
+ }
71
+ const colorFn = chalk[options.color ?? defaultColor];
72
+ // Adopt an existing early spinner if one's running rather than
73
+ // stacking a second one on top.
74
+ if (earlyInstance) {
75
+ this.instance = earlyInstance;
76
+ this.adopted = true;
77
+ earlyInstance = null;
78
+ // Adoption transfers stdout-clear responsibility to this
79
+ // spinner. The early-spinner's stdout interceptor would
80
+ // otherwise still fire (calling stopEarlySpinner with a now-
81
+ // null earlyInstance) while the adopted spinner kept running.
82
+ restoreStdoutWrite();
83
+ this.instance.text = colorFn(options.text);
84
+ return this;
85
+ }
86
+ this.instance = yoctoSpinner({ text: colorFn(options.text) });
87
+ this.instance.start();
88
+ return this;
89
+ }
90
+ /** Release an adopted spinner back to the pool. Returns true if released. */
91
+ releaseAdopted() {
92
+ if (!this.adopted || !this.instance)
93
+ return false;
94
+ earlyInstance = this.instance;
95
+ this.instance = null;
96
+ this.adopted = false;
97
+ return true;
98
+ }
99
+ succeed(text) {
100
+ if (!this.instance)
101
+ return;
102
+ // Release-back is silent; only do it when the caller didn't ask
103
+ // for a visible success line. With text, the user wants the
104
+ // checkmark + message and we terminate.
105
+ if (text === undefined && this.releaseAdopted())
106
+ return;
107
+ // yocto-spinner prepends its own ✔ glyph; passing the bare text
108
+ // (coloured) avoids a duplicated symbol.
109
+ this.instance.success(text ? chalk.green(text) : undefined);
110
+ this.instance = null;
111
+ this.adopted = false;
112
+ }
113
+ fail(text) {
114
+ if (!this.instance)
115
+ return;
116
+ // yocto-spinner prepends its own ✖ glyph; passing the bare text
117
+ // (coloured) avoids a duplicated symbol. Errors always terminate.
118
+ this.instance.error(text ? chalk.red(text) : undefined);
119
+ this.instance = null;
120
+ this.adopted = false;
121
+ }
122
+ stop() {
123
+ if (!this.instance)
124
+ return;
125
+ if (this.releaseAdopted())
126
+ return;
127
+ this.instance.stop();
128
+ this.instance = null;
129
+ }
130
+ }
131
+ async function withSpinner(options, op) {
132
+ const spinner = new LoadingSpinnerImpl().start(options);
133
+ try {
134
+ const result = await op();
135
+ spinner.stop();
136
+ return result;
137
+ }
138
+ catch (error) {
139
+ spinner.fail();
140
+ throw error;
141
+ }
142
+ }
143
+ return {
144
+ LoadingSpinner: LoadingSpinnerImpl,
145
+ withSpinner,
146
+ startEarlySpinner,
147
+ stopEarlySpinner,
148
+ resetEarlySpinner,
149
+ };
150
+ }
151
+ //# sourceMappingURL=spinner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.js","sourceRoot":"","sources":["../src/spinner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ type EmptyOutputConfig = {
2
+ setup: () => void | Promise<void>;
3
+ run: (extraArgs: string[]) => Promise<void>;
4
+ humanMessage: string | RegExp;
5
+ };
6
+ /**
7
+ * Asserts the standard `printEmpty` contract for a command:
8
+ * --json → writes exactly `'[]\n'` to stdout
9
+ * --ndjson → writes nothing to stdout (no stray newline)
10
+ * neither → writes exactly the human message + `\n` to stdout
11
+ *
12
+ * Captures bytes from both `console.log` (which vitest intercepts before
13
+ * it reaches the real stream) and `process.stdout.write`, so commands
14
+ * using either pathway satisfy the contract.
15
+ *
16
+ * Spies are installed AFTER `setup` so any `vi.clearAllMocks()` inside
17
+ * `setup` doesn't clobber them.
18
+ */
19
+ export declare function describeEmptyMachineOutput(label: string, config: EmptyOutputConfig): void;
20
+ export {};
21
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAEA,KAAK,iBAAiB,GAAG;IACrB,KAAK,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,GAAG,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3C,YAAY,EAAE,MAAM,GAAG,MAAM,CAAA;CAChC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CA8CzF"}
@@ -0,0 +1,56 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ /**
3
+ * Asserts the standard `printEmpty` contract for a command:
4
+ * --json → writes exactly `'[]\n'` to stdout
5
+ * --ndjson → writes nothing to stdout (no stray newline)
6
+ * neither → writes exactly the human message + `\n` to stdout
7
+ *
8
+ * Captures bytes from both `console.log` (which vitest intercepts before
9
+ * it reaches the real stream) and `process.stdout.write`, so commands
10
+ * using either pathway satisfy the contract.
11
+ *
12
+ * Spies are installed AFTER `setup` so any `vi.clearAllMocks()` inside
13
+ * `setup` doesn't clobber them.
14
+ */
15
+ export function describeEmptyMachineOutput(label, config) {
16
+ describe(label, () => {
17
+ let captured = '';
18
+ let consoleSpy;
19
+ let writeSpy;
20
+ beforeEach(async () => {
21
+ await config.setup();
22
+ captured = '';
23
+ consoleSpy = vi.spyOn(console, 'log').mockImplementation((...args) => {
24
+ captured += `${args.map((a) => (typeof a === 'string' ? a : String(a))).join(' ')}\n`;
25
+ });
26
+ writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(((chunk) => {
27
+ captured += typeof chunk === 'string' ? chunk : chunk.toString();
28
+ return true;
29
+ }));
30
+ });
31
+ afterEach(() => {
32
+ consoleSpy?.mockRestore();
33
+ writeSpy?.mockRestore();
34
+ consoleSpy = undefined;
35
+ writeSpy = undefined;
36
+ });
37
+ it('writes exactly "[]\\n" to stdout for --json', async () => {
38
+ await config.run(['--json']);
39
+ expect(captured).toBe('[]\n');
40
+ });
41
+ it('writes nothing to stdout for --ndjson (no stray newline)', async () => {
42
+ await config.run(['--ndjson']);
43
+ expect(captured).toBe('');
44
+ });
45
+ it('writes exactly the human message to stdout when no machine flag is set', async () => {
46
+ await config.run([]);
47
+ if (typeof config.humanMessage === 'string') {
48
+ expect(captured).toBe(`${config.humanMessage}\n`);
49
+ }
50
+ else {
51
+ expect(captured).toMatch(config.humanMessage);
52
+ }
53
+ });
54
+ });
55
+ }
56
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAQxE;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAa,EAAE,MAAyB;IAC/E,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE;QACjB,IAAI,QAAQ,GAAG,EAAE,CAAA;QACjB,IAAI,UAAmD,CAAA;QACvD,IAAI,QAAiD,CAAA;QAErD,UAAU,CAAC,KAAK,IAAI,EAAE;YAClB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;YACpB,QAAQ,GAAG,EAAE,CAAA;YACb,UAAU,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,CAAC,GAAG,IAAe,EAAE,EAAE;gBAC5E,QAAQ,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAA;YACzF,CAAC,CAAC,CAAA;YACF,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAC7D,KAA0B,EACnB,EAAE;gBACT,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAA;gBAChE,OAAO,IAAI,CAAA;YACf,CAAC,CAAgC,CAAC,CAAA;QACtC,CAAC,CAAC,CAAA;QAEF,SAAS,CAAC,GAAG,EAAE;YACX,UAAU,EAAE,WAAW,EAAE,CAAA;YACzB,QAAQ,EAAE,WAAW,EAAE,CAAA;YACvB,UAAU,GAAG,SAAS,CAAA;YACtB,QAAQ,GAAG,SAAS,CAAA;QACxB,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;YAC5B,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACjC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;YACtE,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAA;YAC9B,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAC7B,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;YACpF,MAAM,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACpB,IAAI,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;gBAC1C,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,YAAY,IAAI,CAAC,CAAA;YACrD,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;YACjD,CAAC;QACL,CAAC,CAAC,CAAA;IACN,CAAC,CAAC,CAAA;AACN,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/cli-core",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Shared core utilities for Doist CLI projects",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,6 +9,10 @@
9
9
  ".": {
10
10
  "types": "./dist/index.d.ts",
11
11
  "import": "./dist/index.js"
12
+ },
13
+ "./testing": {
14
+ "types": "./dist/testing.d.ts",
15
+ "import": "./dist/testing.js"
12
16
  }
13
17
  },
14
18
  "scripts": {
@@ -58,5 +62,17 @@
58
62
  "semantic-release": "25.0.3",
59
63
  "typescript": "6.0.3",
60
64
  "vitest": "4.1.5"
65
+ },
66
+ "dependencies": {
67
+ "chalk": "5.6.2",
68
+ "yocto-spinner": "1.1.0"
69
+ },
70
+ "peerDependencies": {
71
+ "vitest": ">=4.1"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "vitest": {
75
+ "optional": true
76
+ }
61
77
  }
62
78
  }