@theholocron/kyber 0.0.0-stage → 5.0.0-alpha.338

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/dist/ui.d.mts ADDED
@@ -0,0 +1,151 @@
1
+ /** A JSON-pointer-ish path: `""` is the root, children append `/<key>`. */
2
+ type Path = string;
3
+ /** One rendered line of the viewer. */
4
+ interface Line {
5
+ /** Where this line's value sits in the document. */
6
+ path: Path;
7
+ /** The line as drawn, indent included. */
8
+ text: string;
9
+ /** Object/array with at least one child — the only lines that fold. */
10
+ foldable: boolean;
11
+ /** Whether this line's children are shown. */
12
+ expanded: boolean;
13
+ /** Number of leading indent levels, so the TTY layer can jump to a parent. */
14
+ level: number;
15
+ }
16
+ /** What {@link renderTree} needs to draw the document. */
17
+ interface RenderOptions {
18
+ /** Paths currently expanded. */
19
+ expanded: ReadonlySet<Path>;
20
+ }
21
+ /** A value that can have children: an object or an array. */
22
+ type Container = Record<string, unknown> | unknown[];
23
+ /** How {@link viewJson} shows a document. */
24
+ interface ViewJsonOptions {
25
+ /** Collapse containers deeper than this many levels (static mode and the interactive start state). */
26
+ depth?: number;
27
+ /**
28
+ * `true` forces the folding viewer (throws without a TTY), `false` forces
29
+ * static output. Left unset it auto-enables when stdin and stdout are both
30
+ * TTYs and no `depth` was asked for (an explicit depth means static output).
31
+ */
32
+ interactive?: boolean;
33
+ }
34
+ /** Where the interactive viewer is: the selected line, the first visible line and the open containers. */
35
+ interface ViewState {
36
+ /** The index of the selected line. */
37
+ cursor: number;
38
+ /** The index of the first line on screen. */
39
+ top: number;
40
+ /** The paths currently open. */
41
+ expanded: Set<Path>;
42
+ }
43
+ /** A key the interactive viewer reacts to, already mapped from the terminal's escape sequences. */
44
+ type Key = "up" | "down" | "left" | "right" | "expandAll" | "collapseAll" | "quit";
45
+ /**
46
+ * Paths of every container deeper than `depth` levels are left out, so those
47
+ * render collapsed. `depth: 0` collapses the root; `Infinity` expands all.
48
+ */
49
+ export declare function expandToDepth(value: unknown, depth: number): Set<Path>;
50
+ /** Flatten `value` into the lines visible under `expanded`. Pure — no I/O. */
51
+ export declare function renderTree(value: unknown, { expanded }: RenderOptions): Line[];
52
+ /** Pure key reducer: `(state, key) -> state`. `rows` is the viewport height. */
53
+ export declare function reduceKey(state: ViewState, key: Key, value: unknown, rows: number): ViewState;
54
+ /**
55
+ * Print `value` as JSON. Non-TTY stdout always gets plain, valid
56
+ * `JSON.stringify` output (so `| jq` and redirects keep working); a TTY gets a
57
+ * coloured tree, collapsed past `depth`. `interactive` opens a folding viewer (the default on a TTY).
58
+ */
59
+ export declare function viewJson(value: unknown, options?: ViewJsonOptions): Promise<void>;
60
+ /**
61
+ * Raised when a prompt is needed but stdin isn't a TTY. A non-TTY run (CI, a
62
+ * script, a pipe) throws this instead of hanging on `@inquirer/prompts`, which
63
+ * otherwise throws its own `ExitPromptError` the moment stdin closes. `cli.ts`'s
64
+ * `USER_FACING_ERRORS` catch prints the message cleanly.
65
+ */
66
+ export declare class NonInteractiveError extends Error {
67
+ /** The error's name, `NonInteractiveError`, so a catch can tell it from any other error. */
68
+ name: string;
69
+ }
70
+ /**
71
+ * Open `path` in the OS's default file browser (Finder / Explorer /
72
+ * whatever `xdg-open` resolves to on Linux). Fire-and-forget — detached so
73
+ * it outlives this process, and never throws when the opener binary isn't
74
+ * on PATH (e.g. no `xdg-open` in a minimal container).
75
+ */
76
+ export declare function openPath(path: string): void;
77
+ /** A spinner for one step at a time, which the step can print through without breaking it. */
78
+ interface Progress {
79
+ /** Run `fn` under a spinner named `label`. The spinner is cleared, not kept, so the step's own result line stands alone. */
80
+ run<T>(label: string, fn: () => Promise<T>): Promise<T>;
81
+ /** Print a line without breaking the spinner: it is cleared, the line printed, and the spinner drawn again. */
82
+ print(line: string): void;
83
+ }
84
+ /**
85
+ * Hooks something into a running step, such as a retry notice. It is given the step's label and a way to change the
86
+ * spinner's text, and returns the function that unhooks it, called when the step ends.
87
+ */
88
+ type ProgressListener = (label: string, setText: (text: string) => void) => () => void;
89
+ /**
90
+ * Runs `fn`, showing an ora spinner for its duration in TTY environments.
91
+ * In non-TTY environments (CI, pipes, tests) the spinner is skipped entirely.
92
+ */
93
+ export declare function withSpinner<T>(label: string, fn: () => Promise<T>): Promise<T>;
94
+ /** Turn progress off for the run (`--quiet`, `--log`), so nothing extra shares the terminal with what those print. */
95
+ export declare function allowProgress(on: boolean): void;
96
+ /**
97
+ * Whether to draw a progress line: only when a person is watching a terminal (stdout and stderr both), not in CI, and not
98
+ * switched off. Everywhere else the output stays the plain line-per-step text that scripts and the docs rely on.
99
+ */
100
+ export declare function progressEnabled(): boolean;
101
+ /**
102
+ * A spinner for one step at a time that a step can print through. Steps that only return a result use {@link
103
+ * withProgress}; a step that prints its own result line while it runs prints through `print`. A `listen` hook can change the
104
+ * spinner's text while the step runs, for instance to say that a request is being sent again.
105
+ */
106
+ export declare function createProgress(print: (line: string) => void, enabled?: () => boolean, listen?: ProgressListener): Progress;
107
+ /** `fn` under a cleared-when-done spinner named `label`, on a terminal only. */
108
+ export declare function withProgress<T>(label: string, fn: () => Promise<T>, listen?: ProgressListener): Promise<T>;
109
+ /** How a row of a check or setup run came out: it passed, it failed, or it was skipped. */
110
+ type RowStatus = "ok" | "fail" | "skip";
111
+ /** Right-pads `s` to `width` with spaces — never truncates. */
112
+ export declare function pad(s: string, width: number): string;
113
+ /**
114
+ * One row of a status report: a 2-space indent, the status icon (✓ / ✗ / ·),
115
+ * then `columns` joined with a two-space gap (blank columns dropped, so a
116
+ * row that has nothing for its last column doesn't trail whitespace).
117
+ * Column padding (`pad`) is the caller's job — `doctor` pads capability and
118
+ * provider to a fixed width; `clone`'s repo names and paths don't, since
119
+ * they vary too widely for fixed-width alignment to read cleanly.
120
+ *
121
+ * Shared by `doctor` (capability / provider / message) and `clone` (repo /
122
+ * destination / reason) so every status table in the CLI reads the same way.
123
+ */
124
+ export declare function statusRow(status: RowStatus, columns: string[]): string;
125
+ /** Formats a message with a status marker and colour: one helper per kind of line a command prints. */
126
+ export declare const style: {
127
+ /** A green `✓` for something that worked. */
128
+ success: (msg: string) => string;
129
+ /** A yellow `⚠` for something worth a second look. */
130
+ warn: (msg: string) => string;
131
+ /** A red `✗` for something that failed. */
132
+ fail: (msg: string) => string;
133
+ /** A cyan `→` for a step being taken. */
134
+ step: (msg: string) => string;
135
+ /** A dim `·` marker for a row that's neither a pass nor a failure — loaded but inactive, or skipped. */
136
+ skip: (msg: string) => string;
137
+ /** Dimmed text for a suggestion or a next step. */
138
+ hint: (msg: string) => string;
139
+ /** Dimmed text for detail that should recede. */
140
+ dim: (msg: string) => string;
141
+ /** Bold text for a heading. */
142
+ header: (msg: string) => string;
143
+ };
144
+ /**
145
+ * Print `message` and wait until Enter is pressed, the input ends, or Ctrl-C is pressed, whichever comes first, and
146
+ * return which: `enter`, or `closed` for the other two. It never hangs on a closed input. It reads from stdin and
147
+ * writes to stderr, so a pipe on stdout is never touched; the caller checks there is a terminal to answer it. The
148
+ * streams are parameters so a test can answer for the user.
149
+ */
150
+ export declare function waitForEnter(message: string, input?: NodeJS.ReadableStream, output?: NodeJS.WritableStream): Promise<"enter" | "closed">;
151
+ export type { Container, Key, Line, Path, Progress, ProgressListener, RenderOptions, RowStatus, ViewJsonOptions, ViewState };
package/dist/ui.mjs ADDED
@@ -0,0 +1,391 @@
1
+ import { t as NonInteractiveError } from "./non-interactive-error-Mm_Mn79M.mjs";
2
+ import chalk from "chalk";
3
+ import { spawn } from "node:child_process";
4
+ import ora from "ora";
5
+ import { createInterface } from "node:readline";
6
+ // src/ui/json-view/json-view.ts
7
+ function isContainer(value) {
8
+ return typeof value === "object" && value !== null;
9
+ }
10
+ function childrenOf(value) {
11
+ return Array.isArray(value) ? value.map((v, i) => [String(i), v]) : Object.entries(value);
12
+ }
13
+ function scalar(value) {
14
+ if (typeof value === "string") return chalk.green(JSON.stringify(value));
15
+ if (typeof value === "number") return chalk.yellow(String(value));
16
+ if (typeof value === "boolean") return chalk.magenta(String(value));
17
+ return chalk.dim(String(value === void 0 ? null : value));
18
+ }
19
+ function summary(value) {
20
+ const n = childrenOf(value).length;
21
+ const unit = Array.isArray(value) ? n === 1 ? "item" : "items" : n === 1 ? "key" : "keys";
22
+ return chalk.dim(Array.isArray(value) ? `[… ${n} ${unit}]` : `{… ${n} ${unit}}`);
23
+ }
24
+ /**
25
+ * Paths of every container deeper than `depth` levels are left out, so those
26
+ * render collapsed. `depth: 0` collapses the root; `Infinity` expands all.
27
+ */
28
+ function expandToDepth(value, depth) {
29
+ const out = /* @__PURE__ */ new Set();
30
+ const walk = (v, path, level) => {
31
+ if (!isContainer(v) || level >= depth) return;
32
+ out.add(path);
33
+ for (const [k, child] of childrenOf(v)) walk(child, `${path}/${k}`, level + 1);
34
+ };
35
+ walk(value, "", 0);
36
+ return out;
37
+ }
38
+ /** Flatten `value` into the lines visible under `expanded`. Pure — no I/O. */
39
+ function renderTree(value, { expanded }) {
40
+ const lines = [];
41
+ const walk = (v, path, level, label, trailing) => {
42
+ const indent = " ".repeat(level);
43
+ const prefix = `${indent}${label}`;
44
+ if (!isContainer(v)) {
45
+ lines.push({
46
+ path,
47
+ text: `${prefix}${scalar(v)}${trailing}`,
48
+ foldable: false,
49
+ expanded: false,
50
+ level
51
+ });
52
+ return;
53
+ }
54
+ const kids = childrenOf(v);
55
+ const [open, close] = Array.isArray(v) ? ["[", "]"] : ["{", "}"];
56
+ if (kids.length === 0) {
57
+ lines.push({
58
+ path,
59
+ text: `${prefix}${open}${close}${trailing}`,
60
+ foldable: false,
61
+ expanded: false,
62
+ level
63
+ });
64
+ return;
65
+ }
66
+ if (!expanded.has(path)) {
67
+ lines.push({
68
+ path,
69
+ text: `${prefix}${summary(v)}${trailing}`,
70
+ foldable: true,
71
+ expanded: false,
72
+ level
73
+ });
74
+ return;
75
+ }
76
+ lines.push({
77
+ path,
78
+ text: `${prefix}${open}`,
79
+ foldable: true,
80
+ expanded: true,
81
+ level
82
+ });
83
+ kids.forEach(([k, child], i) => {
84
+ const comma = i < kids.length - 1 ? "," : "";
85
+ const childLabel = Array.isArray(v) ? "" : `${chalk.cyan(JSON.stringify(k))}: `;
86
+ walk(child, `${path}/${k}`, level + 1, childLabel, comma);
87
+ });
88
+ lines.push({
89
+ path: `${path}#close`,
90
+ text: `${indent}${close}${trailing}`,
91
+ foldable: false,
92
+ expanded: false,
93
+ level
94
+ });
95
+ };
96
+ walk(value, "", 0, "", "");
97
+ return lines;
98
+ }
99
+ /** Pure key reducer: `(state, key) -> state`. `rows` is the viewport height. */
100
+ function reduceKey(state, key, value, rows) {
101
+ const lines = renderTree(value, { expanded: state.expanded });
102
+ const cur = lines[state.cursor];
103
+ let { cursor } = state;
104
+ const expanded = new Set(state.expanded);
105
+ switch (key) {
106
+ case "up":
107
+ cursor = Math.max(0, cursor - 1);
108
+ break;
109
+ case "down":
110
+ cursor = Math.min(lines.length - 1, cursor + 1);
111
+ break;
112
+ case "right":
113
+ if (cur?.foldable) expanded.add(cur.path);
114
+ break;
115
+ case "left":
116
+ if (cur?.foldable && cur.expanded) expanded.delete(cur.path);
117
+ else if (cur) {
118
+ const parent = lines.findLastIndex((l, i) => i < cursor && l.foldable && l.expanded && l.level < cur.level);
119
+ if (parent >= 0) cursor = parent;
120
+ }
121
+ break;
122
+ case "expandAll": return {
123
+ cursor,
124
+ top: state.top,
125
+ expanded: expandToDepth(value, Infinity)
126
+ };
127
+ case "collapseAll": return {
128
+ cursor: 0,
129
+ top: 0,
130
+ expanded: /* @__PURE__ */ new Set()
131
+ };
132
+ }
133
+ const count = renderTree(value, { expanded }).length;
134
+ cursor = Math.min(cursor, count - 1);
135
+ let top = state.top;
136
+ if (cursor < top) top = cursor;
137
+ if (cursor >= top + rows) top = cursor - rows + 1;
138
+ return {
139
+ cursor,
140
+ top: Math.max(0, top),
141
+ expanded
142
+ };
143
+ }
144
+ const KEYMAP = {
145
+ "\x1B[A": "up",
146
+ k: "up",
147
+ "\x1B[B": "down",
148
+ j: "down",
149
+ "\x1B[C": "right",
150
+ l: "right",
151
+ "\r": "right",
152
+ " ": "right",
153
+ "\x1B[D": "left",
154
+ h: "left",
155
+ E: "expandAll",
156
+ C: "collapseAll",
157
+ q: "quit",
158
+ "\x1B": "quit",
159
+ "": "quit"
160
+ };
161
+ function draw(out, value, state, rows) {
162
+ const lines = renderTree(value, { expanded: state.expanded });
163
+ const view = lines.slice(state.top, state.top + rows).map((l, i) => {
164
+ const row = `${l.foldable ? l.expanded ? "▾ " : "▸ " : " "}${l.text}`;
165
+ return state.top + i === state.cursor ? chalk.inverse(row) : row;
166
+ });
167
+ const footer = chalk.dim(`${state.cursor + 1}/${lines.length} ↑↓ move →/⏎ expand ← collapse E/C all q quit`);
168
+ out.write(`\u001b[H\u001b[J${view.join("\n")}\n${footer}`);
169
+ }
170
+ async function runInteractive(value, depth) {
171
+ const { stdin, stdout } = process;
172
+ if (!stdin.isTTY || !stdout.isTTY) throw new NonInteractiveError("--interactive needs a TTY on stdin and stdout.");
173
+ const rows = () => Math.max(3, (stdout.rows ?? 24) - 1);
174
+ let state = {
175
+ cursor: 0,
176
+ top: 0,
177
+ expanded: expandToDepth(value, depth)
178
+ };
179
+ const wasRaw = stdin.isRaw;
180
+ stdin.setRawMode(true);
181
+ stdin.resume();
182
+ stdout.write("\x1B[?1049h\x1B[?25l");
183
+ const redraw = () => draw(stdout, value, state, rows());
184
+ stdout.on("resize", redraw);
185
+ try {
186
+ redraw();
187
+ await new Promise((resolve) => {
188
+ const onData = (buf) => {
189
+ const key = KEYMAP[buf.toString()];
190
+ if (key === "quit") {
191
+ stdin.off("data", onData);
192
+ resolve();
193
+ return;
194
+ }
195
+ if (key) {
196
+ state = reduceKey(state, key, value, rows());
197
+ redraw();
198
+ }
199
+ };
200
+ stdin.on("data", onData);
201
+ });
202
+ } finally {
203
+ stdout.off("resize", redraw);
204
+ stdout.write("\x1B[?25h\x1B[?1049l");
205
+ stdin.setRawMode(wasRaw);
206
+ stdin.pause();
207
+ }
208
+ }
209
+ /**
210
+ * Print `value` as JSON. Non-TTY stdout always gets plain, valid
211
+ * `JSON.stringify` output (so `| jq` and redirects keep working); a TTY gets a
212
+ * coloured tree, collapsed past `depth`. `interactive` opens a folding viewer (the default on a TTY).
213
+ */
214
+ async function viewJson(value, options = {}) {
215
+ const depth = options.depth ?? Infinity;
216
+ if (options.interactive ?? (options.depth === void 0 && Boolean(process.stdin.isTTY && process.stdout.isTTY))) return runInteractive(value, options.depth ?? 1);
217
+ if (!process.stdout.isTTY) {
218
+ console.log(JSON.stringify(value, null, 2));
219
+ return;
220
+ }
221
+ const lines = renderTree(value, { expanded: expandToDepth(value, depth) });
222
+ console.log(lines.map((l) => l.text).join("\n"));
223
+ }
224
+ // src/ui/open-path/open-path.ts
225
+ /** Platform-appropriate command to open a path in the OS's file browser. */
226
+ function openCommand(path) {
227
+ if (process.platform === "darwin") return {
228
+ cmd: "open",
229
+ args: [path]
230
+ };
231
+ if (process.platform === "win32") return {
232
+ cmd: "cmd",
233
+ args: [
234
+ "/c",
235
+ "start",
236
+ "",
237
+ path
238
+ ]
239
+ };
240
+ return {
241
+ cmd: "xdg-open",
242
+ args: [path]
243
+ };
244
+ }
245
+ /**
246
+ * Open `path` in the OS's default file browser (Finder / Explorer /
247
+ * whatever `xdg-open` resolves to on Linux). Fire-and-forget — detached so
248
+ * it outlives this process, and never throws when the opener binary isn't
249
+ * on PATH (e.g. no `xdg-open` in a minimal container).
250
+ */
251
+ function openPath(path) {
252
+ const { cmd, args } = openCommand(path);
253
+ const child = spawn(cmd, args, {
254
+ stdio: "ignore",
255
+ detached: true
256
+ });
257
+ child.on("error", () => {});
258
+ child.unref();
259
+ }
260
+ // src/ui/progress/progress.ts
261
+ /**
262
+ * Runs `fn`, showing an ora spinner for its duration in TTY environments.
263
+ * In non-TTY environments (CI, pipes, tests) the spinner is skipped entirely.
264
+ */
265
+ async function withSpinner(label, fn) {
266
+ if (!process.stdout.isTTY) return fn();
267
+ const spinner = ora(label).start();
268
+ try {
269
+ const result = await fn();
270
+ spinner.succeed();
271
+ return result;
272
+ } catch (err) {
273
+ spinner.fail();
274
+ throw err;
275
+ }
276
+ }
277
+ let allowed = true;
278
+ /** Turn progress off for the run (`--quiet`, `--log`), so nothing extra shares the terminal with what those print. */
279
+ function allowProgress(on) {
280
+ allowed = on;
281
+ }
282
+ /**
283
+ * Whether to draw a progress line: only when a person is watching a terminal (stdout and stderr both), not in CI, and not
284
+ * switched off. Everywhere else the output stays the plain line-per-step text that scripts and the docs rely on.
285
+ */
286
+ function progressEnabled() {
287
+ return allowed && Boolean(process.stdout.isTTY) && Boolean(process.stderr.isTTY) && !process.env["CI"];
288
+ }
289
+ /**
290
+ * A spinner for one step at a time that a step can print through. Steps that only return a result use {@link
291
+ * withProgress}; a step that prints its own result line while it runs prints through `print`. A `listen` hook can change the
292
+ * spinner's text while the step runs, for instance to say that a request is being sent again.
293
+ */
294
+ function createProgress(print, enabled = progressEnabled, listen) {
295
+ let active;
296
+ return {
297
+ async run(label, fn) {
298
+ if (!enabled()) return fn();
299
+ const spinner = ora({
300
+ text: label,
301
+ stream: process.stderr,
302
+ discardStdin: false
303
+ }).start();
304
+ active = spinner;
305
+ const stopListening = listen?.(label, (text) => {
306
+ spinner.text = text;
307
+ });
308
+ try {
309
+ return await fn();
310
+ } finally {
311
+ stopListening?.();
312
+ spinner.stop();
313
+ active = void 0;
314
+ }
315
+ },
316
+ print(line) {
317
+ const spinner = active;
318
+ if (!spinner) return print(line);
319
+ spinner.stop();
320
+ print(line);
321
+ spinner.start();
322
+ }
323
+ };
324
+ }
325
+ /** `fn` under a cleared-when-done spinner named `label`, on a terminal only. */
326
+ function withProgress(label, fn, listen) {
327
+ return createProgress(() => {}, progressEnabled, listen).run(label, fn);
328
+ }
329
+ // src/ui/style/style.ts
330
+ /** Formats a message with a status marker and colour: one helper per kind of line a command prints. */
331
+ const style = {
332
+ /** A green `✓` for something that worked. */
333
+ success: (msg) => `${chalk.green("✓")} ${msg}`,
334
+ /** A yellow `⚠` for something worth a second look. */
335
+ warn: (msg) => `${chalk.yellow("⚠")} ${msg}`,
336
+ /** A red `✗` for something that failed. */
337
+ fail: (msg) => `${chalk.red("✗")} ${msg}`,
338
+ /** A cyan `→` for a step being taken. */
339
+ step: (msg) => `${chalk.cyan("→")} ${msg}`,
340
+ /** A dim `·` marker for a row that's neither a pass nor a failure — loaded but inactive, or skipped. */
341
+ skip: (msg) => chalk.dim(`· ${msg}`),
342
+ /** Dimmed text for a suggestion or a next step. */
343
+ hint: (msg) => chalk.dim(msg),
344
+ /** Dimmed text for detail that should recede. */
345
+ dim: (msg) => chalk.dim(msg),
346
+ /** Bold text for a heading. */
347
+ header: (msg) => chalk.bold(msg)
348
+ };
349
+ // src/ui/status-row/status-row.ts
350
+ /** Right-pads `s` to `width` with spaces — never truncates. */
351
+ function pad(s, width) {
352
+ return s.length >= width ? s : s + " ".repeat(width - s.length);
353
+ }
354
+ /**
355
+ * One row of a status report: a 2-space indent, the status icon (✓ / ✗ / ·),
356
+ * then `columns` joined with a two-space gap (blank columns dropped, so a
357
+ * row that has nothing for its last column doesn't trail whitespace).
358
+ * Column padding (`pad`) is the caller's job — `doctor` pads capability and
359
+ * provider to a fixed width; `clone`'s repo names and paths don't, since
360
+ * they vary too widely for fixed-width alignment to read cleanly.
361
+ *
362
+ * Shared by `doctor` (capability / provider / message) and `clone` (repo /
363
+ * destination / reason) so every status table in the CLI reads the same way.
364
+ */
365
+ function statusRow(status, columns) {
366
+ const joined = columns.filter((c) => c.length > 0).join(" ");
367
+ return ` ${status === "ok" ? style.success(joined) : status === "fail" ? style.fail(joined) : style.skip(joined)}`;
368
+ }
369
+ // src/ui/wait-for-enter/wait-for-enter.ts
370
+ /**
371
+ * Print `message` and wait until Enter is pressed, the input ends, or Ctrl-C is pressed, whichever comes first, and
372
+ * return which: `enter`, or `closed` for the other two. It never hangs on a closed input. It reads from stdin and
373
+ * writes to stderr, so a pipe on stdout is never touched; the caller checks there is a terminal to answer it. The
374
+ * streams are parameters so a test can answer for the user.
375
+ */
376
+ function waitForEnter(message, input = process.stdin, output = process.stderr) {
377
+ return new Promise((resolve) => {
378
+ const rl = createInterface({
379
+ input,
380
+ output
381
+ });
382
+ let answered = false;
383
+ rl.on("close", () => resolve(answered ? "enter" : "closed"));
384
+ rl.on("SIGINT", () => rl.close());
385
+ rl.question(message, () => {
386
+ answered = true;
387
+ rl.close();
388
+ });
389
+ });
390
+ }
391
+ export { NonInteractiveError, allowProgress, createProgress, expandToDepth, openPath, pad, progressEnabled, reduceKey, renderTree, statusRow, style, viewJson, waitForEnter, withProgress, withSpinner };
package/package.json CHANGED
@@ -1,6 +1,129 @@
1
1
  {
2
2
  "name": "@theholocron/kyber",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "5.0.0-alpha.338",
4
+ "description": "The reusable parts of a command-line tool: terminal output, prompts, the update notifier, safe handling of tokens, a test harness and the global flags.",
5
+ "keywords": [
6
+ "cli",
7
+ "holocron",
8
+ "kyber",
9
+ "theholocron"
10
+ ],
11
+ "homepage": "https://docs.theholocron.dev/holocron/kyber/",
12
+ "bugs": "https://github.com/theholocron/holocron/issues",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/theholocron/holocron.git",
16
+ "directory": "cli/kyber"
17
+ },
18
+ "license": "MIT",
19
+ "author": "Newton Koumantzelis",
20
+ "sideEffects": false,
21
+ "type": "module",
22
+ "exports": {
23
+ "./flags": {
24
+ "types": "./dist/flags.d.mts",
25
+ "import": "./dist/flags.mjs",
26
+ "default": "./dist/flags.mjs"
27
+ },
28
+ "./notifier": {
29
+ "types": "./dist/notifier.d.mts",
30
+ "import": "./dist/notifier.mjs",
31
+ "default": "./dist/notifier.mjs"
32
+ },
33
+ "./prompts": {
34
+ "types": "./dist/prompts.d.mts",
35
+ "import": "./dist/prompts.mjs",
36
+ "default": "./dist/prompts.mjs"
37
+ },
38
+ "./secrets": {
39
+ "types": "./dist/secrets.d.mts",
40
+ "import": "./dist/secrets.mjs",
41
+ "default": "./dist/secrets.mjs"
42
+ },
43
+ "./testing": {
44
+ "types": "./dist/testing.d.mts",
45
+ "import": "./dist/testing.mjs",
46
+ "default": "./dist/testing.mjs"
47
+ },
48
+ "./ui": {
49
+ "types": "./dist/ui.d.mts",
50
+ "import": "./dist/ui.mjs",
51
+ "default": "./dist/ui.mjs"
52
+ }
53
+ },
54
+ "files": [
55
+ "dist"
56
+ ],
57
+ "devDependencies": {
58
+ "@inquirer/prompts": "^8.7.5",
59
+ "@inquirer/search": "^4.3.6",
60
+ "@types/node": "^26",
61
+ "@types/yargs": "^17.0.35",
62
+ "@vitest/coverage-v8": "^5.0.5",
63
+ "@vitest/eslint-plugin": "^1.6.28",
64
+ "chalk": "^6.0.1",
65
+ "clipboardy": "^5.3.2",
66
+ "dotenv": "^17.4.2",
67
+ "eslint": "^10.14.0",
68
+ "eslint-plugin-n": "^18.5.0",
69
+ "globals": "^17.14.0",
70
+ "ora": "^9.4.1",
71
+ "tsdown": "^0.23.4",
72
+ "typescript": "^5.9.3",
73
+ "vitest": "^5.0.5",
74
+ "yargs": "^18.1.0",
75
+ "@theholocron/eslint-config": "^8.9.0-alpha.8",
76
+ "@theholocron/tsconfig": "^8.9.0-alpha.8",
77
+ "@theholocron/tsdown-config": "^8.9.0-alpha.8",
78
+ "@theholocron/vitest-config": "^8.9.0-alpha.8"
79
+ },
80
+ "peerDependencies": {
81
+ "@inquirer/prompts": "^8.0.0",
82
+ "@inquirer/search": "^4.0.0",
83
+ "@napi-rs/keyring": "^1.0.0",
84
+ "chalk": "^6.0.0",
85
+ "clipboardy": "^5.0.0",
86
+ "dotenv": "^17.0.0",
87
+ "ora": "^9.0.0",
88
+ "yargs": "^18.0.0"
89
+ },
90
+ "peerDependenciesMeta": {
91
+ "@inquirer/prompts": {
92
+ "optional": true
93
+ },
94
+ "@inquirer/search": {
95
+ "optional": true
96
+ },
97
+ "@napi-rs/keyring": {
98
+ "optional": true
99
+ },
100
+ "chalk": {
101
+ "optional": true
102
+ },
103
+ "clipboardy": {
104
+ "optional": true
105
+ },
106
+ "dotenv": {
107
+ "optional": true
108
+ },
109
+ "ora": {
110
+ "optional": true
111
+ },
112
+ "yargs": {
113
+ "optional": true
114
+ }
115
+ },
116
+ "engines": {
117
+ "node": ">=22"
118
+ },
119
+ "publishConfig": {
120
+ "access": "public"
121
+ },
122
+ "scripts": {
123
+ "build": "tsdown",
124
+ "lint:code": "eslint .",
125
+ "test:unit": "vitest run --coverage",
126
+ "test:watch": "vitest",
127
+ "typecheck": "tsc --noEmit"
128
+ }
6
129
  }