@ninhache/probe 0.1.2 → 0.2.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 +32 -2
- package/dist/chunk-M3ASEG5F.js +48 -0
- package/dist/chunk-ZCAUPLQS.js +305 -0
- package/dist/cli.js +355 -182
- package/dist/devtools-QAEP2P5I.js +90 -0
- package/dist/open-TQ6NPJ7V.js +20479 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ $ echo $?
|
|
|
19
19
|
|
|
20
20
|
```sh
|
|
21
21
|
brew install ninhache/tap/probe # macOS, Linux
|
|
22
|
-
npm install -g @ninhache/probe # anywhere with Node >=
|
|
22
|
+
npm install -g @ninhache/probe # anywhere with Node >= 22
|
|
23
23
|
npx @ninhache/probe check # no install
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -41,14 +41,44 @@ Use the full name with `npx`: `npx probe` is another package.
|
|
|
41
41
|
| `probe rm <name>` | Remove a service |
|
|
42
42
|
| `probe ls` | List services |
|
|
43
43
|
| `probe check [name...] [--json] [--timeout <seconds>]` | Check all services, or the named ones |
|
|
44
|
+
| `probe wait [name...] [--json] [--timeout <seconds>]` | Check again until every service is up, or give up after the timeout |
|
|
44
45
|
| `probe status` | CLI version, pending migration, Makefile block state, available update |
|
|
45
46
|
| `probe sync` | Regenerate the Makefile block |
|
|
46
47
|
| `probe migrate` | Migrate `.probe/config.yml` to the current schema |
|
|
47
48
|
|
|
48
49
|
Exit codes: `0` all up, `1` a service is down, `2` usage or config error.
|
|
49
50
|
|
|
51
|
+
`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
|
+
and every 5 s, for 60 s in total. `--timeout` sets the total, `--interval` the first pause, `--max-interval` the
|
|
53
|
+
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. Progress goes to stderr, the final report to stdout; exit code `1` names the services still down.
|
|
55
|
+
|
|
50
56
|
`probe` works from any sub-directory: it walks up to the nearest `.probe/`.
|
|
51
57
|
|
|
58
|
+
## Dashboard
|
|
59
|
+
|
|
60
|
+
`probe` alone, on a terminal, opens a live list of your services and checks them every 2 s
|
|
61
|
+
(`probe --interval 5` for another pace). Off a terminal, in CI or in a pipe, nothing changes: use `probe check`.
|
|
62
|
+
|
|
63
|
+
| Key | What it does |
|
|
64
|
+
| --------------- | ------------------------------------------------ |
|
|
65
|
+
| `↑` `↓` `j` `k` | Move |
|
|
66
|
+
| `r` | Check now |
|
|
67
|
+
| `Enter` | Open the panel of the selected service (skip it) |
|
|
68
|
+
| `o` | Options: interval and timeout |
|
|
69
|
+
| `q` | Quit |
|
|
70
|
+
|
|
71
|
+
What you change there is saved in `.probe/local.yml`, for your machine only:
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
dashboard: { interval: 5, timeout: 3 }
|
|
75
|
+
services:
|
|
76
|
+
db: { skip: true }
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
A skipped service is left out of `probe check`, `probe wait` and the dashboard, and shown as `skipped`. Name it
|
|
80
|
+
(`probe check db`) and it is checked anyway.
|
|
81
|
+
|
|
52
82
|
## Config
|
|
53
83
|
|
|
54
84
|
`.probe/config.yml`:
|
|
@@ -75,7 +105,7 @@ Files in `.probe/`:
|
|
|
75
105
|
|
|
76
106
|
- `config.yml`: services. Commit it.
|
|
77
107
|
- `lock.yml`: CLI version and hash of the Makefile block. Commit it.
|
|
78
|
-
- `local.yml`: per-machine values. Ignored by git.
|
|
108
|
+
- `local.yml`: per-machine values (dashboard settings, skipped services). Ignored by git.
|
|
79
109
|
|
|
80
110
|
Never put secrets in `config.yml` or `lock.yml`.
|
|
81
111
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "node:module";
|
|
2
|
+
const require = __createRequire(import.meta.url);
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
10
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
11
|
+
}) : x)(function(x) {
|
|
12
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
13
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
14
|
+
});
|
|
15
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
16
|
+
try {
|
|
17
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
throw mod = 0, e;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var __export = (target, all) => {
|
|
23
|
+
for (var name in all)
|
|
24
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
25
|
+
};
|
|
26
|
+
var __copyProps = (to, from, except, desc) => {
|
|
27
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
28
|
+
for (let key of __getOwnPropNames(from))
|
|
29
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
30
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
31
|
+
}
|
|
32
|
+
return to;
|
|
33
|
+
};
|
|
34
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
35
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
36
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
37
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
38
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
39
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
40
|
+
mod
|
|
41
|
+
));
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
__require,
|
|
45
|
+
__commonJS,
|
|
46
|
+
__export,
|
|
47
|
+
__toESM
|
|
48
|
+
};
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "node:module";
|
|
2
|
+
const require = __createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/constants.ts
|
|
5
|
+
var PACKAGE_NAME = "@ninhache/probe";
|
|
6
|
+
var PROBE_DIR = ".probe";
|
|
7
|
+
var CONFIG_FILE = "config.yml";
|
|
8
|
+
var LOCK_FILE = "lock.yml";
|
|
9
|
+
var LOCAL_FILE = "local.yml";
|
|
10
|
+
var BACKUP_GLOB = "*.bak.yml";
|
|
11
|
+
var LOCAL_DRAFT_FILE = "local.draft.bak.yml";
|
|
12
|
+
var LOCAL_FILE_MODE = 384;
|
|
13
|
+
var DEFAULT_MAKEFILE = "Makefile";
|
|
14
|
+
var DEFAULT_CHECKER_TYPE = "http";
|
|
15
|
+
var DEFAULT_TIMEOUT_SECONDS = 3;
|
|
16
|
+
var DASHBOARD_INTERVAL_SECONDS = 2;
|
|
17
|
+
var DASHBOARD_INTERVAL_CHOICES_SECONDS = [0.5, 1, 2, 5, 10, 30, 60];
|
|
18
|
+
var DASHBOARD_TIMEOUT_CHOICES_SECONDS = [1, 2, 3, 5, 10, 30];
|
|
19
|
+
var MAX_TIMEOUT_SECONDS = 2147483;
|
|
20
|
+
var DEFAULT_WAIT_TIMEOUT_SECONDS = 60;
|
|
21
|
+
var DEFAULT_WAIT_INTERVAL_SECONDS = 0.5;
|
|
22
|
+
var DEFAULT_WAIT_MAX_INTERVAL_SECONDS = 5;
|
|
23
|
+
var WAIT_BACKOFF_FACTOR = 2;
|
|
24
|
+
var WAIT_LAST_PASS_MS = 1e3;
|
|
25
|
+
var REGISTRY_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
26
|
+
var REGISTRY_TIMEOUT_MS = 5e3;
|
|
27
|
+
var UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
28
|
+
var UPDATE_CACHE_FILE = "latest.json";
|
|
29
|
+
var REFRESH_COMMAND = "__refresh-update-cache";
|
|
30
|
+
var EXIT_DOWN = 1;
|
|
31
|
+
var EXIT_ERROR = 2;
|
|
32
|
+
|
|
33
|
+
// src/errors.ts
|
|
34
|
+
var ProbeError = class extends Error {
|
|
35
|
+
constructor(code, message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.code = code;
|
|
38
|
+
this.name = "ProbeError";
|
|
39
|
+
}
|
|
40
|
+
code;
|
|
41
|
+
exitCode = EXIT_ERROR;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/ui/terminal.ts
|
|
45
|
+
function version(text) {
|
|
46
|
+
const [major, minor] = (text ?? "").split(".").map((part) => Number.parseInt(part, 10));
|
|
47
|
+
return [major || 0, minor || 0];
|
|
48
|
+
}
|
|
49
|
+
function supportsHyperlinks(env) {
|
|
50
|
+
if (env.TMUX || /^(screen|tmux)/.test(env.TERM ?? "")) return false;
|
|
51
|
+
const [major, minor] = version(env.TERM_PROGRAM_VERSION);
|
|
52
|
+
switch (env.TERM_PROGRAM) {
|
|
53
|
+
case "iTerm.app":
|
|
54
|
+
return major > 3 || major === 3 && minor >= 1;
|
|
55
|
+
case "vscode":
|
|
56
|
+
return major > 1 || major === 1 && minor >= 72;
|
|
57
|
+
case "WezTerm":
|
|
58
|
+
case "ghostty":
|
|
59
|
+
case "WarpTerminal":
|
|
60
|
+
case "zed":
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (/^\d{4,}$/.test(env.VTE_VERSION ?? "")) return Number(env.VTE_VERSION) >= 5001;
|
|
64
|
+
return env.TERM === "alacritty" || env.TERM === "xterm-kitty";
|
|
65
|
+
}
|
|
66
|
+
function supportsUnicode(env) {
|
|
67
|
+
if (env.TERM === "linux") return false;
|
|
68
|
+
return /UTF-?8/i.test(env.LC_ALL || env.LC_CTYPE || env.LANG || "");
|
|
69
|
+
}
|
|
70
|
+
function detectTerminal(stream, env) {
|
|
71
|
+
const isTTY = Boolean(stream.isTTY);
|
|
72
|
+
const capable = isTTY && env.TERM !== "dumb";
|
|
73
|
+
const columns = isTTY && stream.columns && stream.columns > 0 ? stream.columns : void 0;
|
|
74
|
+
return {
|
|
75
|
+
isTTY,
|
|
76
|
+
color: capable && !env.NO_COLOR,
|
|
77
|
+
columns,
|
|
78
|
+
hyperlinks: capable && supportsHyperlinks(env),
|
|
79
|
+
unicode: capable && supportsUnicode(env)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function isInteractive(stdin, stdout, env) {
|
|
83
|
+
return Boolean(stdin.isTTY) && Boolean(stdout.isTTY) && env.TERM !== "dumb" && !env.CI;
|
|
84
|
+
}
|
|
85
|
+
var ELLIPSIS = "\u2026";
|
|
86
|
+
function truncate(text, max) {
|
|
87
|
+
const characters = Array.from(text);
|
|
88
|
+
if (characters.length <= max) return text;
|
|
89
|
+
return max <= 1 ? ELLIPSIS : `${characters.slice(0, max - 1).join("")}${ELLIPSIS}`;
|
|
90
|
+
}
|
|
91
|
+
var SGR_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
92
|
+
function stripStyles(text) {
|
|
93
|
+
return text.replace(SGR_PATTERN, "");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/checkers/http.ts
|
|
97
|
+
var REASONS = {
|
|
98
|
+
ECONNREFUSED: "connection refused",
|
|
99
|
+
ECONNRESET: "connection reset",
|
|
100
|
+
ENOTFOUND: "host not found",
|
|
101
|
+
EAI_AGAIN: "host not found",
|
|
102
|
+
EHOSTUNREACH: "host unreachable"
|
|
103
|
+
};
|
|
104
|
+
function targetUrl(service) {
|
|
105
|
+
const bareIpv6 = service.host.includes(":") && !service.host.startsWith("[");
|
|
106
|
+
const host = bareIpv6 ? `[${service.host}]` : service.host;
|
|
107
|
+
return `http://${host}:${service.port}${service.path}`;
|
|
108
|
+
}
|
|
109
|
+
function isExpected(expect, status) {
|
|
110
|
+
if (expect === void 0) return status >= 200 && status < 300;
|
|
111
|
+
return Array.isArray(expect) ? expect.includes(status) : expect === status;
|
|
112
|
+
}
|
|
113
|
+
function expectsRedirect(expect) {
|
|
114
|
+
return [expect ?? []].flat().some((status) => status >= 300 && status < 400);
|
|
115
|
+
}
|
|
116
|
+
function describeExpected(expect) {
|
|
117
|
+
if (expect === void 0) return "2xx";
|
|
118
|
+
return Array.isArray(expect) ? expect.join(" or ") : String(expect);
|
|
119
|
+
}
|
|
120
|
+
function failureReason(error, timeoutMs) {
|
|
121
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
122
|
+
return `timeout after ${timeoutMs / 1e3}s`;
|
|
123
|
+
}
|
|
124
|
+
const cause = error?.cause;
|
|
125
|
+
const code = cause?.code ?? cause?.errors?.[0]?.code;
|
|
126
|
+
if (code) return REASONS[code] ?? code;
|
|
127
|
+
return error instanceof Error ? error.message : String(error);
|
|
128
|
+
}
|
|
129
|
+
var httpChecker = {
|
|
130
|
+
type: "http",
|
|
131
|
+
async check(name, service, { timeoutMs, signal }) {
|
|
132
|
+
const target = targetUrl(service);
|
|
133
|
+
const base = { name, type: "http", target };
|
|
134
|
+
const started = performance.now();
|
|
135
|
+
const elapsed = () => Math.round(performance.now() - started);
|
|
136
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
137
|
+
try {
|
|
138
|
+
const response = await fetch(target, {
|
|
139
|
+
signal: signal ? AbortSignal.any([timeout, signal]) : timeout,
|
|
140
|
+
// Following a redirect hides its status: an expected 3xx could never match.
|
|
141
|
+
redirect: expectsRedirect(service.expect) ? "manual" : "follow"
|
|
142
|
+
});
|
|
143
|
+
const latencyMs = elapsed();
|
|
144
|
+
await response.body?.cancel();
|
|
145
|
+
const { status } = response;
|
|
146
|
+
if (isExpected(service.expect, status)) return { ...base, ok: true, status, latencyMs };
|
|
147
|
+
const error = `expected ${describeExpected(service.expect)}, got ${status}`;
|
|
148
|
+
return { ...base, ok: false, status, latencyMs, error };
|
|
149
|
+
} catch (error) {
|
|
150
|
+
return { ...base, ok: false, latencyMs: elapsed(), error: failureReason(error, timeoutMs) };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/checkers/registry.ts
|
|
156
|
+
var checkers = new Map([httpChecker].map((checker) => [checker.type, checker]));
|
|
157
|
+
function getChecker(type) {
|
|
158
|
+
const checker = checkers.get(type);
|
|
159
|
+
if (!checker) {
|
|
160
|
+
const known = [...checkers.keys()].join(", ");
|
|
161
|
+
throw new ProbeError("CHECKER_UNKNOWN", `unknown service type "${type}". Known: ${known}.`);
|
|
162
|
+
}
|
|
163
|
+
return checker;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/checkers/run.ts
|
|
167
|
+
async function runChecks(config, names, context, skipped = /* @__PURE__ */ new Set()) {
|
|
168
|
+
const known = Object.keys(config.services);
|
|
169
|
+
const unknown = names.filter((name) => !known.includes(name));
|
|
170
|
+
if (unknown.length > 0) {
|
|
171
|
+
throw new ProbeError(
|
|
172
|
+
"SERVICE_UNKNOWN",
|
|
173
|
+
`unknown service: ${unknown.join(", ")}. Known: ${known.join(", ") || "(none)"}.`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
const selected = names.length > 0 ? [...new Set(names)] : known;
|
|
177
|
+
return Promise.all(
|
|
178
|
+
selected.map((name) => {
|
|
179
|
+
const service = config.services[name];
|
|
180
|
+
const type = service.type ?? DEFAULT_CHECKER_TYPE;
|
|
181
|
+
if (names.length === 0 && skipped.has(name)) {
|
|
182
|
+
const result = {
|
|
183
|
+
name,
|
|
184
|
+
type,
|
|
185
|
+
ok: true,
|
|
186
|
+
skipped: true,
|
|
187
|
+
target: targetUrl(service),
|
|
188
|
+
latencyMs: 0
|
|
189
|
+
};
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
return getChecker(type).check(name, service, context);
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/ui/colors.ts
|
|
198
|
+
function createColors(enabled) {
|
|
199
|
+
const wrap = (open, close) => (text) => enabled ? `\x1B[${open}m${text}\x1B[${close}m` : text;
|
|
200
|
+
return {
|
|
201
|
+
green: wrap(32, 39),
|
|
202
|
+
red: wrap(31, 39),
|
|
203
|
+
yellow: wrap(33, 39),
|
|
204
|
+
dim: wrap(2, 22),
|
|
205
|
+
bold: wrap(1, 22)
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/ui/report.ts
|
|
210
|
+
var GAP = " ";
|
|
211
|
+
var SKIPPED = "skipped";
|
|
212
|
+
function width(values) {
|
|
213
|
+
return Math.max(0, ...values.map((value) => value.length));
|
|
214
|
+
}
|
|
215
|
+
function nameBudget(nameWidth, columns, reserved) {
|
|
216
|
+
return Math.max(1, Math.min(nameWidth, Math.floor((columns - reserved) / 2)));
|
|
217
|
+
}
|
|
218
|
+
function formatCheckResults(results, terminal) {
|
|
219
|
+
const colors = createColors(terminal.color);
|
|
220
|
+
const nameWidth = width(results.map((result) => result.name));
|
|
221
|
+
const targetWidth = width(results.map((result) => result.target));
|
|
222
|
+
const outcomes = results.map((result) => {
|
|
223
|
+
if (result.skipped) return SKIPPED;
|
|
224
|
+
return result.ok ? `${result.status}${GAP}${result.latencyMs}ms` : result.error ?? "down";
|
|
225
|
+
});
|
|
226
|
+
const fullWidth = 2 + nameWidth + GAP.length + targetWidth + GAP.length + width(outcomes);
|
|
227
|
+
const { columns } = terminal;
|
|
228
|
+
const compact = columns !== void 0 && fullWidth > columns;
|
|
229
|
+
const nameMax = compact ? nameBudget(nameWidth, columns, 2 + GAP.length) : nameWidth;
|
|
230
|
+
const outcomeMax = compact ? Math.max(1, columns - 2 - nameMax - GAP.length) : void 0;
|
|
231
|
+
return results.map((result, index) => {
|
|
232
|
+
const mark = result.skipped ? colors.dim("-") : result.ok ? colors.green("\u2713") : colors.red("\u2717");
|
|
233
|
+
const name = colors.bold(truncate(result.name, nameMax).padEnd(nameMax));
|
|
234
|
+
const target = compact ? "" : `${colors.dim(result.target.padEnd(targetWidth))}${GAP}`;
|
|
235
|
+
const plain = outcomes[index];
|
|
236
|
+
let outcome;
|
|
237
|
+
if (result.skipped) {
|
|
238
|
+
outcome = colors.dim(outcomeMax === void 0 ? plain : truncate(plain, outcomeMax));
|
|
239
|
+
} else if (!result.ok) {
|
|
240
|
+
outcome = colors.red(outcomeMax === void 0 ? plain : truncate(plain, outcomeMax));
|
|
241
|
+
} else if (outcomeMax !== void 0 && plain.length > outcomeMax) {
|
|
242
|
+
outcome = colors.green(truncate(String(result.status), outcomeMax));
|
|
243
|
+
} else {
|
|
244
|
+
outcome = `${colors.green(String(result.status))}${GAP}${colors.dim(`${result.latencyMs}ms`)}`;
|
|
245
|
+
}
|
|
246
|
+
return `${mark} ${name}${GAP}${target}${outcome}`;
|
|
247
|
+
}).join("\n");
|
|
248
|
+
}
|
|
249
|
+
function formatServices(config, terminal, skipped = /* @__PURE__ */ new Set()) {
|
|
250
|
+
const colors = createColors(terminal.color);
|
|
251
|
+
const rows = Object.entries(config.services).map(([name, service]) => ({
|
|
252
|
+
name,
|
|
253
|
+
url: targetUrl(service),
|
|
254
|
+
notes: (service.expect === void 0 ? "" : `${GAP}expect ${service.expect}`) + (skipped.has(name) ? `${GAP}${SKIPPED}` : "")
|
|
255
|
+
}));
|
|
256
|
+
const nameWidth = width(rows.map((row) => row.name));
|
|
257
|
+
const fullWidth = width(rows.map((row) => " ".repeat(nameWidth) + GAP + row.url + row.notes));
|
|
258
|
+
const { columns } = terminal;
|
|
259
|
+
const compact = columns !== void 0 && fullWidth > columns;
|
|
260
|
+
const nameMax = compact ? nameBudget(nameWidth, columns, GAP.length) : nameWidth;
|
|
261
|
+
const urlMax = compact ? Math.max(1, columns - nameMax - GAP.length) : void 0;
|
|
262
|
+
return rows.map((row) => {
|
|
263
|
+
const name = colors.bold(truncate(row.name, nameMax).padEnd(nameMax));
|
|
264
|
+
const url = urlMax === void 0 ? row.url : truncate(row.url, urlMax);
|
|
265
|
+
return `${name}${GAP}${url}${compact || !row.notes ? "" : colors.dim(row.notes)}`;
|
|
266
|
+
}).join("\n");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export {
|
|
270
|
+
PACKAGE_NAME,
|
|
271
|
+
PROBE_DIR,
|
|
272
|
+
CONFIG_FILE,
|
|
273
|
+
LOCK_FILE,
|
|
274
|
+
LOCAL_FILE,
|
|
275
|
+
BACKUP_GLOB,
|
|
276
|
+
LOCAL_DRAFT_FILE,
|
|
277
|
+
LOCAL_FILE_MODE,
|
|
278
|
+
DEFAULT_MAKEFILE,
|
|
279
|
+
DEFAULT_TIMEOUT_SECONDS,
|
|
280
|
+
DASHBOARD_INTERVAL_SECONDS,
|
|
281
|
+
DASHBOARD_INTERVAL_CHOICES_SECONDS,
|
|
282
|
+
DASHBOARD_TIMEOUT_CHOICES_SECONDS,
|
|
283
|
+
MAX_TIMEOUT_SECONDS,
|
|
284
|
+
DEFAULT_WAIT_TIMEOUT_SECONDS,
|
|
285
|
+
DEFAULT_WAIT_INTERVAL_SECONDS,
|
|
286
|
+
DEFAULT_WAIT_MAX_INTERVAL_SECONDS,
|
|
287
|
+
WAIT_BACKOFF_FACTOR,
|
|
288
|
+
WAIT_LAST_PASS_MS,
|
|
289
|
+
REGISTRY_LATEST_URL,
|
|
290
|
+
REGISTRY_TIMEOUT_MS,
|
|
291
|
+
UPDATE_CHECK_TTL_MS,
|
|
292
|
+
UPDATE_CACHE_FILE,
|
|
293
|
+
REFRESH_COMMAND,
|
|
294
|
+
EXIT_DOWN,
|
|
295
|
+
EXIT_ERROR,
|
|
296
|
+
ProbeError,
|
|
297
|
+
createColors,
|
|
298
|
+
detectTerminal,
|
|
299
|
+
isInteractive,
|
|
300
|
+
truncate,
|
|
301
|
+
stripStyles,
|
|
302
|
+
runChecks,
|
|
303
|
+
formatCheckResults,
|
|
304
|
+
formatServices
|
|
305
|
+
};
|