@ninhache/probe 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/README.md +37 -23
- package/dist/{chunk-ZCAUPLQS.js → chunk-HHTOVIAQ.js} +11 -3
- package/dist/cli.js +54 -24
- package/dist/{open-TQ6NPJ7V.js → open-QTP4Z5QX.js} +48 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,23 +27,25 @@ Use the full name with `npx`: `npx probe` is another package.
|
|
|
27
27
|
|
|
28
28
|
## Quick start
|
|
29
29
|
|
|
30
|
-
1. `probe init` creates `.probe
|
|
30
|
+
1. `probe init` creates `.probe/`.
|
|
31
31
|
2. `probe add api -p 3000 --path /health` declares a service.
|
|
32
|
-
3. `probe check`
|
|
33
|
-
4. Commit `.probe/config.yml
|
|
32
|
+
3. `probe check` checks everything. Exit code is 1 if a service is down.
|
|
33
|
+
4. Commit `.probe/config.yml`.
|
|
34
|
+
|
|
35
|
+
Want `make check` too? See [Makefile](#makefile): it is optional.
|
|
34
36
|
|
|
35
37
|
## Commands
|
|
36
38
|
|
|
37
39
|
| Command | What it does |
|
|
38
40
|
| --------------------------------------------------------- | ---------------------------------------------------------------------- |
|
|
39
|
-
| `probe init` | Create `.probe
|
|
40
|
-
| `probe add <name> -p <port> [--host] [--path] [--expect]` | Add a service
|
|
41
|
+
| `probe init` | Create `.probe/` and the `.gitignore` entries |
|
|
42
|
+
| `probe add <name> -p <port> [--host] [--path] [--expect]` | Add a service (`--timeout <seconds>` gives it its own timeout) |
|
|
41
43
|
| `probe rm <name>` | Remove a service |
|
|
42
44
|
| `probe ls` | List services |
|
|
43
45
|
| `probe check [name...] [--json] [--timeout <seconds>]` | Check all services, or the named ones |
|
|
44
46
|
| `probe wait [name...] [--json] [--timeout <seconds>]` | Check again until every service is up, or give up after the timeout |
|
|
45
47
|
| `probe status` | CLI version, pending migration, Makefile block state, available update |
|
|
46
|
-
| `probe sync` |
|
|
48
|
+
| `probe sync` | Add or regenerate the Makefile block (optional) |
|
|
47
49
|
| `probe migrate` | Migrate `.probe/config.yml` to the current schema |
|
|
48
50
|
|
|
49
51
|
Exit codes: `0` all up, `1` a service is down, `2` usage or config error.
|
|
@@ -51,7 +53,8 @@ Exit codes: `0` all up, `1` a service is down, `2` usage or config error.
|
|
|
51
53
|
`probe wait` is for scripts: `probe wait && pnpm test`. It checks at once, then again after 0.5 s, 1 s, 2 s, 4 s
|
|
52
54
|
and every 5 s, for 60 s in total. `--timeout` sets the total, `--interval` the first pause, `--max-interval` the
|
|
53
55
|
longest one. Unlike `check --timeout`, which is the time given to each service, `wait --timeout` covers the whole
|
|
54
|
-
wait; each check inside it still gets 3 s
|
|
56
|
+
wait; each check inside it still gets 3 s, or the service's own `timeout`. Progress goes to stderr, the final
|
|
57
|
+
report to stdout; exit code `1` names the services still down.
|
|
55
58
|
|
|
56
59
|
`probe` works from any sub-directory: it walks up to the nearest `.probe/`.
|
|
57
60
|
|
|
@@ -60,13 +63,13 @@ wait; each check inside it still gets 3 s. Progress goes to stderr, the final re
|
|
|
60
63
|
`probe` alone, on a terminal, opens a live list of your services and checks them every 2 s
|
|
61
64
|
(`probe --interval 5` for another pace). Off a terminal, in CI or in a pipe, nothing changes: use `probe check`.
|
|
62
65
|
|
|
63
|
-
| Key | What it does
|
|
64
|
-
| --------------- |
|
|
65
|
-
| `↑` `↓` `j` `k` | Move
|
|
66
|
-
| `r` | Check now
|
|
67
|
-
| `Enter` | Open the panel of the selected service
|
|
68
|
-
| `o` | Options: interval and timeout
|
|
69
|
-
| `q` | Quit
|
|
66
|
+
| Key | What it does |
|
|
67
|
+
| --------------- | -------------------------------------- |
|
|
68
|
+
| `↑` `↓` `j` `k` | Move |
|
|
69
|
+
| `r` | Check now |
|
|
70
|
+
| `Enter` | Open the panel of the selected service |
|
|
71
|
+
| `o` | Options: interval and timeout |
|
|
72
|
+
| `q` | Quit |
|
|
70
73
|
|
|
71
74
|
What you change there is saved in `.probe/local.yml`, for your machine only:
|
|
72
75
|
|
|
@@ -79,6 +82,9 @@ services:
|
|
|
79
82
|
A skipped service is left out of `probe check`, `probe wait` and the dashboard, and shown as `skipped`. Name it
|
|
80
83
|
(`probe check db`) and it is checked anyway.
|
|
81
84
|
|
|
85
|
+
The panel of a service also sets its timeout. That one is shared: it is written to `.probe/config.yml`, so
|
|
86
|
+
commit it.
|
|
87
|
+
|
|
82
88
|
## Config
|
|
83
89
|
|
|
84
90
|
`.probe/config.yml`:
|
|
@@ -89,14 +95,19 @@ services:
|
|
|
89
95
|
api: { port: 3000, path: /health }
|
|
90
96
|
front: { port: 5173 }
|
|
91
97
|
admin: { host: 127.0.0.1, port: 8080, expect: [200, 204] }
|
|
98
|
+
db: { port: 5432, timeout: 10 }
|
|
92
99
|
```
|
|
93
100
|
|
|
94
|
-
| Key
|
|
95
|
-
|
|
|
96
|
-
| `port`
|
|
97
|
-
| `host`
|
|
98
|
-
| `path`
|
|
99
|
-
| `expect`
|
|
101
|
+
| Key | Default | Meaning |
|
|
102
|
+
| --------- | ----------- | --------------------------------------- |
|
|
103
|
+
| `port` | required | TCP port |
|
|
104
|
+
| `host` | `localhost` | Host name or IP |
|
|
105
|
+
| `path` | `/` | Path requested with `GET` |
|
|
106
|
+
| `expect` | any 2xx | Expected status, or a list of statuses |
|
|
107
|
+
| `timeout` | 3 | Seconds given to this service to answer |
|
|
108
|
+
|
|
109
|
+
Which timeout applies: `--timeout` when you pass it, else the service's `timeout`, else 3 s (in the dashboard,
|
|
110
|
+
the timeout set in its options). A config with a `timeout` needs `probe` 0.4.0 or later: older versions refuse it.
|
|
100
111
|
|
|
101
112
|
Redirects are followed and the final status is judged, unless `expect` contains a 3xx: then the first response
|
|
102
113
|
is judged as is.
|
|
@@ -104,15 +115,15 @@ is judged as is.
|
|
|
104
115
|
Files in `.probe/`:
|
|
105
116
|
|
|
106
117
|
- `config.yml`: services. Commit it.
|
|
107
|
-
- `lock.yml`: CLI version and hash of the Makefile block. Commit it.
|
|
118
|
+
- `lock.yml`: CLI version and hash of the Makefile block. Only there once `probe sync` has run. Commit it.
|
|
108
119
|
- `local.yml`: per-machine values (dashboard settings, skipped services). Ignored by git.
|
|
109
120
|
|
|
110
121
|
Never put secrets in `config.yml` or `lock.yml`.
|
|
111
122
|
|
|
112
123
|
## Makefile
|
|
113
124
|
|
|
114
|
-
|
|
115
|
-
never touched.
|
|
125
|
+
Optional. `probe sync` writes a block between two markers, in a new `Makefile` or at the end of yours. Everything
|
|
126
|
+
outside the block is yours and is never touched. Without a block, no command creates or changes a `Makefile`.
|
|
116
127
|
|
|
117
128
|
```make
|
|
118
129
|
# >>> probe (generated - do not edit, run `probe sync`)
|
|
@@ -120,6 +131,9 @@ never touched.
|
|
|
120
131
|
# <<< probe
|
|
121
132
|
```
|
|
122
133
|
|
|
134
|
+
Once the block is there, `probe add` and `probe rm` keep it up to date. Commit the `Makefile` and
|
|
135
|
+
`.probe/lock.yml`.
|
|
136
|
+
|
|
123
137
|
Targets: `help`, `check`, `status` and one `check-<name>` per service. The block uses `probe` when it is in
|
|
124
138
|
your `PATH`, else `npx -y @ninhache/probe@latest`. It works with GNU Make 3.81 (the macOS default).
|
|
125
139
|
|
|
@@ -164,7 +164,12 @@ function getChecker(type) {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
// src/checkers/run.ts
|
|
167
|
-
|
|
167
|
+
function resolveTimeoutMs(service, options) {
|
|
168
|
+
const own = options.forceTimeout || service.timeout === void 0 ? void 0 : service.timeout;
|
|
169
|
+
const wanted = own === void 0 ? options.timeoutMs : Math.ceil(own * 1e3);
|
|
170
|
+
return Math.max(1, Math.min(wanted, options.maxTimeoutMs ?? wanted));
|
|
171
|
+
}
|
|
172
|
+
async function runChecks(config, names, options, skipped = /* @__PURE__ */ new Set()) {
|
|
168
173
|
const known = Object.keys(config.services);
|
|
169
174
|
const unknown = names.filter((name) => !known.includes(name));
|
|
170
175
|
if (unknown.length > 0) {
|
|
@@ -189,7 +194,10 @@ async function runChecks(config, names, context, skipped = /* @__PURE__ */ new S
|
|
|
189
194
|
};
|
|
190
195
|
return result;
|
|
191
196
|
}
|
|
192
|
-
return getChecker(type).check(name, service,
|
|
197
|
+
return getChecker(type).check(name, service, {
|
|
198
|
+
timeoutMs: resolveTimeoutMs(service, options),
|
|
199
|
+
signal: options.signal
|
|
200
|
+
});
|
|
193
201
|
})
|
|
194
202
|
);
|
|
195
203
|
}
|
|
@@ -251,7 +259,7 @@ function formatServices(config, terminal, skipped = /* @__PURE__ */ new Set()) {
|
|
|
251
259
|
const rows = Object.entries(config.services).map(([name, service]) => ({
|
|
252
260
|
name,
|
|
253
261
|
url: targetUrl(service),
|
|
254
|
-
notes: (service.expect === void 0 ? "" : `${GAP}expect ${service.expect}`) + (skipped.has(name) ? `${GAP}${SKIPPED}` : "")
|
|
262
|
+
notes: (service.expect === void 0 ? "" : `${GAP}expect ${service.expect}`) + (service.timeout === void 0 ? "" : `${GAP}timeout ${service.timeout}s`) + (skipped.has(name) ? `${GAP}${SKIPPED}` : "")
|
|
255
263
|
}));
|
|
256
264
|
const nameWidth = width(rows.map((row) => row.name));
|
|
257
265
|
const fullWidth = width(rows.map((row) => " ".repeat(nameWidth) + GAP + row.url + row.notes));
|
package/dist/cli.js
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
isInteractive,
|
|
35
35
|
runChecks,
|
|
36
36
|
stripStyles
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-HHTOVIAQ.js";
|
|
38
38
|
import "./chunk-M3ASEG5F.js";
|
|
39
39
|
|
|
40
40
|
// src/cli.ts
|
|
@@ -98,7 +98,9 @@ var serviceSchemaV1 = z.strictObject({
|
|
|
98
98
|
host: z.string().min(1).default("localhost"),
|
|
99
99
|
port: z.number().int().min(1).max(65535),
|
|
100
100
|
path: z.string().startsWith("/").default("/"),
|
|
101
|
-
expect: z.union([statusCode, z.array(statusCode).min(1)]).optional()
|
|
101
|
+
expect: z.union([statusCode, z.array(statusCode).min(1)]).optional(),
|
|
102
|
+
/** Seconds given to this service to answer, instead of the default. */
|
|
103
|
+
timeout: z.number().positive().max(MAX_TIMEOUT_SECONDS).optional()
|
|
102
104
|
});
|
|
103
105
|
var configSchemaV1 = z.strictObject({
|
|
104
106
|
schemaVersion: z.literal(1),
|
|
@@ -278,6 +280,15 @@ async function saveConfig(loaded, raw, options = {}) {
|
|
|
278
280
|
await writeFile(loaded.paths.config, loaded.doc.toString());
|
|
279
281
|
return { ...loaded, config, raw };
|
|
280
282
|
}
|
|
283
|
+
async function writeServiceTimeout(paths, name, seconds2) {
|
|
284
|
+
const loaded = await loadConfig(paths);
|
|
285
|
+
const services = loaded.raw.services ?? {};
|
|
286
|
+
if (!Object.hasOwn(services, name)) return false;
|
|
287
|
+
const { timeout: _previous, ...service } = services[name];
|
|
288
|
+
if (seconds2 !== void 0) service.timeout = seconds2;
|
|
289
|
+
await saveConfig(loaded, { ...loaded.raw, services: { ...services, [name]: service } });
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
281
292
|
|
|
282
293
|
// src/makefile/sync.ts
|
|
283
294
|
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
@@ -305,7 +316,7 @@ async function writeLock(paths, lock) {
|
|
|
305
316
|
}
|
|
306
317
|
|
|
307
318
|
// src/version.ts
|
|
308
|
-
var CLI_VERSION = true ? "0.
|
|
319
|
+
var CLI_VERSION = true ? "0.4.0" : "0.0.0-dev";
|
|
309
320
|
|
|
310
321
|
// src/makefile/generator.ts
|
|
311
322
|
import { createHash } from "crypto";
|
|
@@ -419,9 +430,10 @@ function rawServices(loaded) {
|
|
|
419
430
|
return loaded.raw.services ?? {};
|
|
420
431
|
}
|
|
421
432
|
async function saveServices(loaded, services) {
|
|
422
|
-
const
|
|
433
|
+
const block = await blockState(loaded.paths, loaded.config);
|
|
423
434
|
const saved = await saveConfig(loaded, { ...loaded.raw, services });
|
|
424
|
-
if (
|
|
435
|
+
if (block === "missing") return;
|
|
436
|
+
if (block !== "modified") {
|
|
425
437
|
await syncMakefile(saved.paths, saved.config);
|
|
426
438
|
return;
|
|
427
439
|
}
|
|
@@ -448,7 +460,11 @@ var add_default = defineCommand({
|
|
|
448
460
|
port: { type: "string", alias: "p", description: "Port", required: true },
|
|
449
461
|
host: { type: "string", description: "Host (default: localhost)" },
|
|
450
462
|
path: { type: "string", description: "Path to request (default: /)" },
|
|
451
|
-
expect: { type: "string", description: "Expected status, e.g. 200 or 200,204 (default: 2xx)" }
|
|
463
|
+
expect: { type: "string", description: "Expected status, e.g. 200 or 200,204 (default: 2xx)" },
|
|
464
|
+
timeout: {
|
|
465
|
+
type: "string",
|
|
466
|
+
description: `Seconds to wait for this service (default: ${DEFAULT_TIMEOUT_SECONDS})`
|
|
467
|
+
}
|
|
452
468
|
},
|
|
453
469
|
async run({ args }) {
|
|
454
470
|
const loaded = await openProject();
|
|
@@ -463,6 +479,10 @@ var add_default = defineCommand({
|
|
|
463
479
|
if (args.host) service.host = args.host;
|
|
464
480
|
if (args.path) service.path = args.path;
|
|
465
481
|
if (args.expect) service.expect = parseExpect(String(args.expect));
|
|
482
|
+
if (args.timeout !== void 0) {
|
|
483
|
+
secondsToMs("--timeout", String(args.timeout));
|
|
484
|
+
service.timeout = Number(args.timeout);
|
|
485
|
+
}
|
|
466
486
|
await saveServices(loaded, { ...services, [args.name]: service });
|
|
467
487
|
console.log(`Added ${args.name}. Try: probe check ${args.name}`);
|
|
468
488
|
}
|
|
@@ -582,15 +602,16 @@ var check_default = defineCommand2({
|
|
|
582
602
|
json: { type: "boolean", description: "Print results as JSON" },
|
|
583
603
|
timeout: {
|
|
584
604
|
type: "string",
|
|
585
|
-
|
|
586
|
-
default:
|
|
605
|
+
// No citty default: a value given here must be told apart from none, it beats the services' own.
|
|
606
|
+
description: `Seconds to wait for each service (default: its own timeout, else ${DEFAULT_TIMEOUT_SECONDS})`
|
|
587
607
|
}
|
|
588
608
|
},
|
|
589
609
|
async run({ args }) {
|
|
590
|
-
const
|
|
610
|
+
const forceTimeout = args.timeout !== void 0;
|
|
611
|
+
const timeoutMs = secondsToMs("--timeout", args.timeout ?? String(DEFAULT_TIMEOUT_SECONDS));
|
|
591
612
|
const { config, paths } = await openProject();
|
|
592
613
|
const skipped = await readSkippedServices(paths);
|
|
593
|
-
const results = await runChecks(config, args._, { timeoutMs }, skipped);
|
|
614
|
+
const results = await runChecks(config, args._, { timeoutMs, forceTimeout }, skipped);
|
|
594
615
|
if (args.json) {
|
|
595
616
|
console.log(JSON.stringify(results, null, 2));
|
|
596
617
|
} else if (results.length === 0) {
|
|
@@ -625,7 +646,7 @@ async function ignoreLocalFiles(root) {
|
|
|
625
646
|
var init_default = defineCommand3({
|
|
626
647
|
meta: {
|
|
627
648
|
name: "init",
|
|
628
|
-
description: "Create .probe
|
|
649
|
+
description: "Create .probe/ and the .gitignore entries"
|
|
629
650
|
},
|
|
630
651
|
async run() {
|
|
631
652
|
const paths = pathsFor(process.cwd());
|
|
@@ -635,9 +656,7 @@ var init_default = defineCommand3({
|
|
|
635
656
|
await mkdir(paths.dir);
|
|
636
657
|
await writeFile5(paths.config, INITIAL_CONFIG);
|
|
637
658
|
await ignoreLocalFiles(paths.root);
|
|
638
|
-
|
|
639
|
-
await syncMakefile(paths, config);
|
|
640
|
-
console.log(`Created ${PROBE_DIR}/ and the Makefile block. Next: probe add api -p 3000`);
|
|
659
|
+
console.log(`Created ${PROBE_DIR}/. Next: probe add api -p 3000`);
|
|
641
660
|
}
|
|
642
661
|
});
|
|
643
662
|
|
|
@@ -768,7 +787,7 @@ function describeBlock(state, colors) {
|
|
|
768
787
|
case "outdated":
|
|
769
788
|
return colors.yellow("out of date \u2192 run `probe sync`");
|
|
770
789
|
case "missing":
|
|
771
|
-
return colors.
|
|
790
|
+
return colors.dim("no probe block (optional) \u2192 `probe sync` adds one");
|
|
772
791
|
}
|
|
773
792
|
}
|
|
774
793
|
function describeUpdate(colors) {
|
|
@@ -802,7 +821,10 @@ var status_default = defineCommand7({
|
|
|
802
821
|
import { relative as relative2 } from "path";
|
|
803
822
|
import { defineCommand as defineCommand8 } from "citty";
|
|
804
823
|
var sync_default = defineCommand8({
|
|
805
|
-
meta: {
|
|
824
|
+
meta: {
|
|
825
|
+
name: "sync",
|
|
826
|
+
description: "Add or regenerate the probe block of the Makefile (optional)"
|
|
827
|
+
},
|
|
806
828
|
async run() {
|
|
807
829
|
const { config, paths } = await openProject();
|
|
808
830
|
const { created, changed } = await syncMakefile(paths, config);
|
|
@@ -827,15 +849,16 @@ async function pollUntilUp(check, options) {
|
|
|
827
849
|
let pause = Math.min(options.intervalMs, maxIntervalMs);
|
|
828
850
|
let lastPass = false;
|
|
829
851
|
while (true) {
|
|
830
|
-
const
|
|
852
|
+
const left = Math.max(1, Math.ceil(remaining()));
|
|
853
|
+
const results = await check(Math.min(attemptTimeoutMs, left), left);
|
|
831
854
|
const elapsedMs = Math.round(now() - started);
|
|
832
855
|
onPass?.(results, elapsedMs);
|
|
833
856
|
if (results.every((result) => result.ok)) return { results, elapsedMs, timedOut: false };
|
|
834
|
-
const
|
|
835
|
-
if (lastPass ||
|
|
836
|
-
if (pause >=
|
|
857
|
+
const rest = remaining();
|
|
858
|
+
if (lastPass || rest <= 0) return { results, elapsedMs, timedOut: true };
|
|
859
|
+
if (pause >= rest) {
|
|
837
860
|
lastPass = true;
|
|
838
|
-
await sleep(
|
|
861
|
+
await sleep(rest - Math.min(WAIT_LAST_PASS_MS, rest / 2));
|
|
839
862
|
} else {
|
|
840
863
|
await sleep(pause);
|
|
841
864
|
pause = Math.min(pause * WAIT_BACKOFF_FACTOR, maxIntervalMs);
|
|
@@ -899,7 +922,13 @@ var wait_default = defineCommand9({
|
|
|
899
922
|
const skipped = await readSkippedServices(paths);
|
|
900
923
|
const report = progressReporter();
|
|
901
924
|
const { results, timedOut } = await pollUntilUp(
|
|
902
|
-
|
|
925
|
+
// A service with its own timeout keeps it here, within what is left of the total.
|
|
926
|
+
(_passMs, remainingMs) => runChecks(
|
|
927
|
+
config,
|
|
928
|
+
args._,
|
|
929
|
+
{ timeoutMs: DEFAULT_TIMEOUT_SECONDS * 1e3, maxTimeoutMs: remainingMs },
|
|
930
|
+
skipped
|
|
931
|
+
),
|
|
903
932
|
{
|
|
904
933
|
deadlineMs,
|
|
905
934
|
attemptTimeoutMs: DEFAULT_TIMEOUT_SECONDS * 1e3,
|
|
@@ -985,7 +1014,7 @@ async function openedDashboard(rawArgs) {
|
|
|
985
1014
|
if (!asked) return false;
|
|
986
1015
|
throw new ProbeError("USAGE", "No services yet. Add one: probe add api -p 3000");
|
|
987
1016
|
}
|
|
988
|
-
const { openDashboard } = await import("./open-
|
|
1017
|
+
const { openDashboard } = await import("./open-QTP4Z5QX.js");
|
|
989
1018
|
let settings = initialDashboardSettings(askedMs, await readDashboardPreferences(paths));
|
|
990
1019
|
let saving = Promise.resolve();
|
|
991
1020
|
const save = (write) => {
|
|
@@ -1000,7 +1029,8 @@ async function openedDashboard(rawArgs) {
|
|
|
1000
1029
|
settings = next;
|
|
1001
1030
|
save(() => writeDashboardPreferences(paths, changed));
|
|
1002
1031
|
},
|
|
1003
|
-
onSkipChange: (name, skip) => save(() => writeServiceSkip(paths, name, skip))
|
|
1032
|
+
onSkipChange: (name, skip) => save(() => writeServiceSkip(paths, name, skip)),
|
|
1033
|
+
onTimeoutChange: (name, seconds2) => save(() => writeServiceTimeout(paths, name, seconds2))
|
|
1004
1034
|
});
|
|
1005
1035
|
await saving;
|
|
1006
1036
|
return true;
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
formatCheckResults,
|
|
8
8
|
runChecks,
|
|
9
9
|
truncate
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HHTOVIAQ.js";
|
|
11
11
|
import {
|
|
12
12
|
__commonJS,
|
|
13
13
|
__export,
|
|
@@ -20252,6 +20252,12 @@ var SETTING_ROWS = [
|
|
|
20252
20252
|
{ key: "intervalMs", label: "Interval", choicesMs: DASHBOARD_INTERVAL_CHOICES_SECONDS.map(toMs) },
|
|
20253
20253
|
{ key: "timeoutMs", label: "Timeout", choicesMs: DASHBOARD_TIMEOUT_CHOICES_SECONDS.map(toMs) }
|
|
20254
20254
|
];
|
|
20255
|
+
function stepServiceTimeout(current, direction) {
|
|
20256
|
+
const choices = DASHBOARD_TIMEOUT_CHOICES_SECONDS;
|
|
20257
|
+
if (current === void 0) return direction === 1 ? choices[0] : void 0;
|
|
20258
|
+
if (direction === 1) return choices.find((choice) => choice > current) ?? current;
|
|
20259
|
+
return choices.findLast((choice) => choice < current);
|
|
20260
|
+
}
|
|
20255
20261
|
function stepChoice(choices, current, direction) {
|
|
20256
20262
|
const next = direction === 1 ? choices.find((choice) => choice > current) : choices.findLast((choice) => choice < current);
|
|
20257
20263
|
return next ?? current;
|
|
@@ -20259,7 +20265,9 @@ function stepChoice(choices, current, direction) {
|
|
|
20259
20265
|
|
|
20260
20266
|
// src/dashboard/Dashboard.tsx
|
|
20261
20267
|
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
20262
|
-
var SERVICE_ROWS = ["Skip"];
|
|
20268
|
+
var SERVICE_ROWS = ["Skip", "Timeout"];
|
|
20269
|
+
var TIMEOUT_ROW = SERVICE_ROWS.indexOf("Timeout");
|
|
20270
|
+
var NO_TIMEOUTS = {};
|
|
20263
20271
|
var MARKER_WIDTH = 2;
|
|
20264
20272
|
var PANEL_WIDTH = 30;
|
|
20265
20273
|
var PANEL_FRAME_WIDTH = 4;
|
|
@@ -20268,9 +20276,11 @@ function Dashboard({
|
|
|
20268
20276
|
terminal,
|
|
20269
20277
|
settings: initial,
|
|
20270
20278
|
skipped: initiallySkipped,
|
|
20279
|
+
timeouts: initialTimeouts = NO_TIMEOUTS,
|
|
20271
20280
|
check: check2,
|
|
20272
20281
|
onSettingsChange,
|
|
20273
|
-
onSkipChange
|
|
20282
|
+
onSkipChange,
|
|
20283
|
+
onTimeoutChange
|
|
20274
20284
|
}) {
|
|
20275
20285
|
const { exit } = useApp();
|
|
20276
20286
|
const { columns } = use_window_size_default();
|
|
@@ -20278,6 +20288,7 @@ function Dashboard({
|
|
|
20278
20288
|
const [selected, setSelected] = (0, import_react36.useState)(0);
|
|
20279
20289
|
const [settings, setSettings] = (0, import_react36.useState)(initial);
|
|
20280
20290
|
const [skipped, setSkipped] = (0, import_react36.useState)(() => new Set(initiallySkipped));
|
|
20291
|
+
const [timeouts, setTimeouts] = (0, import_react36.useState)(initialTimeouts);
|
|
20281
20292
|
const [panel, setPanel] = (0, import_react36.useState)(null);
|
|
20282
20293
|
const skipPause = (0, import_react36.useRef)(() => {
|
|
20283
20294
|
});
|
|
@@ -20287,7 +20298,7 @@ function Dashboard({
|
|
|
20287
20298
|
const { signal } = controller;
|
|
20288
20299
|
const loop = async () => {
|
|
20289
20300
|
while (!signal.aborted) {
|
|
20290
|
-
const next = await check2(signal, timeoutMs, skipped);
|
|
20301
|
+
const next = await check2(signal, timeoutMs, skipped, timeouts);
|
|
20291
20302
|
if (signal.aborted) return;
|
|
20292
20303
|
setLatest(next);
|
|
20293
20304
|
await new Promise((resolve) => {
|
|
@@ -20302,7 +20313,7 @@ function Dashboard({
|
|
|
20302
20313
|
};
|
|
20303
20314
|
void loop();
|
|
20304
20315
|
return () => controller.abort();
|
|
20305
|
-
}, [check2, intervalMs, timeoutMs, skipped]);
|
|
20316
|
+
}, [check2, intervalMs, timeoutMs, skipped, timeouts]);
|
|
20306
20317
|
const last = (latest?.length ?? 1) - 1;
|
|
20307
20318
|
const current = Math.min(selected, last);
|
|
20308
20319
|
const change = (rowIndex, direction) => {
|
|
@@ -20321,6 +20332,12 @@ function Dashboard({
|
|
|
20321
20332
|
setSkipped(next);
|
|
20322
20333
|
onSkipChange?.(name, skip);
|
|
20323
20334
|
};
|
|
20335
|
+
const changeTimeout = (name, direction) => {
|
|
20336
|
+
const value = stepServiceTimeout(timeouts[name], direction);
|
|
20337
|
+
if (value === timeouts[name]) return;
|
|
20338
|
+
setTimeouts({ ...timeouts, [name]: value });
|
|
20339
|
+
onTimeoutChange?.(name, value);
|
|
20340
|
+
};
|
|
20324
20341
|
use_input_default((input, key) => {
|
|
20325
20342
|
const up2 = key.upArrow || input === "k";
|
|
20326
20343
|
const down = key.downArrow || input === "j";
|
|
@@ -20342,6 +20359,7 @@ function Dashboard({
|
|
|
20342
20359
|
setPanel({ ...panel, row: Math.max(0, Math.min(lastRow, panel.row + (down ? 1 : -1))) });
|
|
20343
20360
|
} else if (left || right) {
|
|
20344
20361
|
if (panel.kind === "options") change(panel.row, right ? 1 : -1);
|
|
20362
|
+
else if (panel.row === TIMEOUT_ROW) changeTimeout(panel.name, right ? 1 : -1);
|
|
20345
20363
|
else toggleSkip(panel.name);
|
|
20346
20364
|
}
|
|
20347
20365
|
});
|
|
@@ -20371,7 +20389,13 @@ function Dashboard({
|
|
|
20371
20389
|
}).split("\n");
|
|
20372
20390
|
const focused = results[current];
|
|
20373
20391
|
const panelWidth = Math.min(PANEL_WIDTH, columns);
|
|
20374
|
-
const panelRows = panel?.kind === "options" ? SETTING_ROWS.map((row) => [row.label, seconds(settings[row.key])]) : panel ?
|
|
20392
|
+
const panelRows = panel?.kind === "options" ? SETTING_ROWS.map((row) => [row.label, seconds(settings[row.key])]) : panel ? [
|
|
20393
|
+
["Skip", skipped.has(panel.name) ? "yes" : "no"],
|
|
20394
|
+
[
|
|
20395
|
+
"Timeout",
|
|
20396
|
+
timeouts[panel.name] === void 0 ? "default" : `${timeouts[panel.name]}s`
|
|
20397
|
+
]
|
|
20398
|
+
] : [];
|
|
20375
20399
|
const labelWidth = Math.max(0, ...panelRows.map(([label]) => label.length));
|
|
20376
20400
|
const hint = panel === null ? `${unicode ? "\u2191\u2193 jk" : "jk"} move ${dot} r refresh ${dot} ${unicode ? "\u21B5" : "enter"} service ${dot} o options ${dot} q quit` : `${unicode ? "\u2191\u2193 jk" : "jk"} move ${dot} ${unicode ? "\u2190\u2192 hl" : "hl"} change ${dot} esc close`;
|
|
20377
20401
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
@@ -20411,10 +20435,24 @@ function Dashboard({
|
|
|
20411
20435
|
// src/dashboard/open.tsx
|
|
20412
20436
|
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
20413
20437
|
async function openDashboard(config, terminal, state) {
|
|
20414
|
-
const check2 = (signal, timeoutMs, skipped) =>
|
|
20415
|
-
|
|
20416
|
-
|
|
20417
|
-
|
|
20438
|
+
const check2 = (signal, timeoutMs, skipped, timeouts2) => {
|
|
20439
|
+
const services = Object.fromEntries(
|
|
20440
|
+
Object.entries(config.services).map(([name, service]) => [
|
|
20441
|
+
name,
|
|
20442
|
+
{ ...service, timeout: timeouts2[name] }
|
|
20443
|
+
])
|
|
20444
|
+
);
|
|
20445
|
+
return runChecks({ ...config, services }, [], { timeoutMs, signal }, skipped);
|
|
20446
|
+
};
|
|
20447
|
+
const timeouts = Object.fromEntries(
|
|
20448
|
+
Object.entries(config.services).flatMap(
|
|
20449
|
+
([name, { timeout }]) => timeout === void 0 ? [] : [[name, timeout]]
|
|
20450
|
+
)
|
|
20451
|
+
);
|
|
20452
|
+
const app = render_default(
|
|
20453
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Dashboard, { terminal, check: check2, timeouts, ...state }),
|
|
20454
|
+
{ alternateScreen: true }
|
|
20455
|
+
);
|
|
20418
20456
|
await app.waitUntilExit();
|
|
20419
20457
|
}
|
|
20420
20458
|
export {
|