@rse/specbook 1.2.6 → 1.2.7
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 +16 -12
- package/dst/specbook-api.d.ts +3 -2
- package/dst/specbook-api.js +5 -5
- package/dst/specbook-cli.js +21 -13
- package/dst/specbook-cmd-export.js +2 -2
- package/dst/specbook-cmd-lint.js +12 -7
- package/dst/specbook-cmd-preview.js +5 -5
- package/dst/specbook-export-pdf.js +2 -2
- package/dst/specbook-format.md +2 -1
- package/dst/specbook-verbose.d.ts +5 -1
- package/dst/specbook-verbose.js +22 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -131,17 +131,17 @@ Commands:
|
|
|
131
131
|
|
|
132
132
|
```bash
|
|
133
133
|
$ specbook init \
|
|
134
|
-
[-v|--verbose] \
|
|
134
|
+
[-v|--verbose [<level>]] \
|
|
135
135
|
[-c|--config <schema-yaml-file>] \
|
|
136
136
|
[-b|--basedir <spec-md-file-basedir>]
|
|
137
137
|
|
|
138
138
|
$ specbook lint \
|
|
139
|
-
[-v|--verbose] \
|
|
139
|
+
[-v|--verbose [<level>]] \
|
|
140
140
|
[-c|--config <schema-yaml-file>] \
|
|
141
141
|
[-b|--basedir <spec-md-file-basedir>]
|
|
142
142
|
|
|
143
143
|
$ specbook export \
|
|
144
|
-
[-v|--verbose] \
|
|
144
|
+
[-v|--verbose [<level>]] \
|
|
145
145
|
[-c|--config <schema-yaml-file>] \
|
|
146
146
|
[-b|--basedir <spec-md-file-basedir>] \
|
|
147
147
|
[-o|--output [<format>:]<output-file>] \
|
|
@@ -149,14 +149,14 @@ $ specbook export \
|
|
|
149
149
|
[...]
|
|
150
150
|
|
|
151
151
|
$ specbook preview \
|
|
152
|
-
[-v|--verbose] \
|
|
152
|
+
[-v|--verbose [<level>]] \
|
|
153
153
|
[-c|--config <schema-yaml-file>] \
|
|
154
154
|
[-b|--basedir <spec-md-file-basedir>] \
|
|
155
155
|
[-a|--addr <ip-addr>] \
|
|
156
156
|
[-p|--port <tcp-port>]
|
|
157
157
|
|
|
158
158
|
$ specbook describe \
|
|
159
|
-
[-v|--verbose] \
|
|
159
|
+
[-v|--verbose [<level>]] \
|
|
160
160
|
[-c|--config <schema-yaml-file>] \
|
|
161
161
|
[-b|--basedir <spec-md-file-basedir>] \
|
|
162
162
|
[-e|--embed] \
|
|
@@ -166,13 +166,17 @@ $ specbook describe \
|
|
|
166
166
|
[-o|--output <output-file>]
|
|
167
167
|
|
|
168
168
|
$ specbook mcp \
|
|
169
|
-
[-v|--verbose]
|
|
169
|
+
[-v|--verbose [<level>]]
|
|
170
170
|
```
|
|
171
171
|
|
|
172
172
|
Options:
|
|
173
173
|
|
|
174
|
-
- `-v|--verbose`:
|
|
175
|
-
Enable verbose logging of processing information to `stderr
|
|
174
|
+
- `-v|--verbose [<level>]`:
|
|
175
|
+
Enable verbose logging of processing information to `stderr` at the
|
|
176
|
+
given verbosity level: `0` (the default) logs the notices only, `1`
|
|
177
|
+
(the bare flag) also the regular processing information, `2` also
|
|
178
|
+
the reference coverage ratios, and `3` also the unreferenced objects
|
|
179
|
+
of a reference coverage.
|
|
176
180
|
|
|
177
181
|
- `-c|--config <schema-yaml-file>`:
|
|
178
182
|
The YAML schema configuration (default: the bundled standard schema
|
|
@@ -185,10 +189,10 @@ Options:
|
|
|
185
189
|
all diagnostics and fail on any error among them (a warning, like a
|
|
186
190
|
lapse of the reference coverage a `referenced` object kind demands,
|
|
187
191
|
is reported only), so a partial or invalid specification is never
|
|
188
|
-
exported. The verbose output additionally states
|
|
189
|
-
coverage ratios: the ones the `referenced` object
|
|
190
|
-
the ones the `coverage` object kinds report, the
|
|
191
|
-
unreferenced objects
|
|
192
|
+
exported. The verbose output additionally states at verbosity level
|
|
193
|
+
`2` the reference coverage ratios: the ones the `referenced` object
|
|
194
|
+
kinds receive and the ones the `coverage` object kinds report, the
|
|
195
|
+
latter naming the unreferenced objects at level `3`.
|
|
192
196
|
The option accepts glob patterns and can be given multiple times: the
|
|
193
197
|
matching files (in the order of the patterns and alphabetically within
|
|
194
198
|
a pattern, where the literal `std` names the bundled standard schema
|
package/dst/specbook-api.d.ts
CHANGED
|
@@ -3,8 +3,9 @@ import { type LintResult } from "./specbook-cmd-lint.js";
|
|
|
3
3
|
import { parseOutputSpec, formats, type ExportFormat } from "./specbook-cmd-export.js";
|
|
4
4
|
import { previewAddr, previewPort } from "./specbook-cmd-preview.js";
|
|
5
5
|
import { describeFormats, describeParts, parseDescribeFormat, parseDescribePart, compressLevels, parseCompressLevel, type DescribeFormat, type DescribePart, type CompressLevel } from "./specbook-cmd-describe.js";
|
|
6
|
-
import { literal, renderVerbose, type Verbose, type VerboseLevel } from "./specbook-verbose.js";
|
|
7
|
-
export { literal, renderVerbose,
|
|
6
|
+
import { literal, renderVerbose, verbosities, verbosityOf, parseVerbosity, type Verbose, type VerboseLevel, type Verbosity } from "./specbook-verbose.js";
|
|
7
|
+
export { literal, renderVerbose, verbosities, verbosityOf, parseVerbosity };
|
|
8
|
+
export { type Verbose, type VerboseLevel, type Verbosity };
|
|
8
9
|
export { formats, parseOutputSpec, type ExportFormat };
|
|
9
10
|
export { previewAddr, previewPort };
|
|
10
11
|
export { describeFormats, describeParts, parseDescribeFormat, parseDescribePart };
|
package/dst/specbook-api.js
CHANGED
|
@@ -14,9 +14,9 @@ import { lint } from "./specbook-cmd-lint.js";
|
|
|
14
14
|
import { exportSpecification, watchSpecification, parseOutputSpec, formats, requireBrowser } from "./specbook-cmd-export.js";
|
|
15
15
|
import { servePreview, previewAddr, previewPort } from "./specbook-cmd-preview.js";
|
|
16
16
|
import { describeFormat, describeFormats, describeParts, parseDescribeFormat, parseDescribePart, compressLevels, parseCompressLevel } from "./specbook-cmd-describe.js";
|
|
17
|
-
import { literal, renderVerbose } from "./specbook-verbose.js";
|
|
17
|
+
import { literal, renderVerbose, verbosities, verbosityOf, parseVerbosity } from "./specbook-verbose.js";
|
|
18
18
|
/* re-export the central types for API consumers */
|
|
19
|
-
export { literal, renderVerbose };
|
|
19
|
+
export { literal, renderVerbose, verbosities, verbosityOf, parseVerbosity };
|
|
20
20
|
export { formats, parseOutputSpec };
|
|
21
21
|
export { previewAddr, previewPort };
|
|
22
22
|
export { describeFormats, describeParts, parseDescribeFormat, parseDescribePart };
|
|
@@ -37,7 +37,7 @@ export class SpecBook {
|
|
|
37
37
|
}
|
|
38
38
|
/* bind the verbose sink to a particular command */
|
|
39
39
|
verboseOf(cmd) {
|
|
40
|
-
return (msg, level = "
|
|
40
|
+
return (msg, level = "notice") => this.verbose(cmd, msg, level);
|
|
41
41
|
}
|
|
42
42
|
/* determine the files of the YAML schema configuration out of the
|
|
43
43
|
glob patterns, in the order of the patterns and alphabetically
|
|
@@ -96,7 +96,7 @@ export class SpecBook {
|
|
|
96
96
|
throw new Error("invalid specification:\n" +
|
|
97
97
|
result.diagnostics.map(renderDiagnostic).join("\n"));
|
|
98
98
|
for (const diagnostic of result.diagnostics)
|
|
99
|
-
verbose(renderDiagnostic(diagnostic), "
|
|
99
|
+
verbose(renderDiagnostic(diagnostic), "none");
|
|
100
100
|
if (result.specification.artifacts.length === 0)
|
|
101
101
|
throw new Error("unexportable specification: no artifacts found");
|
|
102
102
|
const buffers = new Array();
|
|
@@ -149,7 +149,7 @@ export class SpecBook {
|
|
|
149
149
|
}
|
|
150
150
|
catch (err) {
|
|
151
151
|
verbose("export failed: " +
|
|
152
|
-
(err instanceof Error ? err.message : String(err)), "
|
|
152
|
+
(err instanceof Error ? err.message : String(err)), "none");
|
|
153
153
|
}
|
|
154
154
|
return files;
|
|
155
155
|
}, verbose);
|
package/dst/specbook-cli.js
CHANGED
|
@@ -8,19 +8,23 @@ import * as fs from "node:fs";
|
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import { Command, CommanderError } from "commander";
|
|
10
10
|
import chalk from "chalk";
|
|
11
|
-
import { SpecBook, renderDiagnostic, renderDiagnosticVerbose, renderVerbose, literal, parseOutputSpec, previewAddr, previewPort, describeFormats, describeParts, parseDescribeFormat, parseDescribePart, parseCompressLevel, version } from "./specbook-api.js";
|
|
11
|
+
import { SpecBook, renderDiagnostic, renderDiagnosticVerbose, renderVerbose, literal, parseOutputSpec, previewAddr, previewPort, describeFormats, describeParts, parseDescribeFormat, parseDescribePart, parseCompressLevel, parseVerbosity, verbosityOf, version } from "./specbook-api.js";
|
|
12
12
|
import { serveMcp } from "./specbook-mcp.js";
|
|
13
13
|
/* route verbose messages to stderr, keeping stdout reserved for the
|
|
14
14
|
command outputs and the MCP protocol, and qualify every message with
|
|
15
15
|
the tool name plus the scope path of the emitting command, where
|
|
16
16
|
Chalk styles the output only if the terminal supports colors; the
|
|
17
|
-
|
|
17
|
+
verbosity of the verbose option gates the messages by their level,
|
|
18
|
+
so the "none" messages pass regardless of the option, as they
|
|
18
19
|
report environment problems the user has to see */
|
|
19
|
-
const verboseOf = (opts, ...scope) =>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
const verboseOf = (opts, ...scope) => {
|
|
21
|
+
const verbosity = parseVerbosity(opts.verbose);
|
|
22
|
+
return (cmd, msg, level) => {
|
|
23
|
+
if (verbosityOf[level] <= verbosity) {
|
|
24
|
+
const scopePath = [...scope, cmd].map((segment) => chalk.bold(segment)).join(": ");
|
|
25
|
+
process.stderr.write(`specbook: ${scopePath}: ${renderVerbose(msg, chalk.blue)}\n`);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
24
28
|
};
|
|
25
29
|
/* write a buffer to a standard stream, awaiting the write callback
|
|
26
30
|
which only fires once the data has been flushed to the underlying
|
|
@@ -41,7 +45,7 @@ const writeOutput = async (output, data, cmd, verbose) => {
|
|
|
41
45
|
await writeStdout(data);
|
|
42
46
|
else {
|
|
43
47
|
await fs.promises.writeFile(output, data);
|
|
44
|
-
verbose(cmd, `wrote "${literal(output)}" (${literal(Buffer.byteLength(data))} bytes)`, "
|
|
48
|
+
verbose(cmd, `wrote "${literal(output)}" (${literal(Buffer.byteLength(data))} bytes)`, "notice");
|
|
45
49
|
}
|
|
46
50
|
};
|
|
47
51
|
/* determine an option default value, overridable via a
|
|
@@ -56,9 +60,13 @@ const envDefaultFlag = (name, fallback) => {
|
|
|
56
60
|
const value = process.env[`SPECBOOK_${name.toUpperCase().replace(/-/g, "_")}`];
|
|
57
61
|
return value !== undefined ? !(/^(?:|0|false|no|off)$/i).test(value) : fallback;
|
|
58
62
|
};
|
|
59
|
-
/* provide the verbose option of all sub-commands
|
|
63
|
+
/* provide the verbose option of all sub-commands, whose optional level
|
|
64
|
+
selects the verbosity (where the environment default carries a level
|
|
65
|
+
or a boolean word) */
|
|
60
66
|
const withVerboseOption = (command) => command
|
|
61
|
-
.option("-v, --verbose", "print verbose processing information to stderr
|
|
67
|
+
.option("-v, --verbose [level]", "print verbose processing information to stderr " +
|
|
68
|
+
"(0: none, 1: notices, 2: also the details like the coverage ratios, " +
|
|
69
|
+
"3: also the traces like the unreferenced objects; default: 0, bare flag: 1)", envDefault("verbose", "0"));
|
|
62
70
|
/* provide the repeatable schema configuration option, whose files or
|
|
63
71
|
glob patterns are merged in order (with "std" naming the bundled
|
|
64
72
|
standard one), and determine its value, where the environment default
|
|
@@ -96,7 +104,7 @@ program.name("specbook")
|
|
|
96
104
|
withVerboseOption(program.command("mcp"))
|
|
97
105
|
.description("run as MCP stdio server")
|
|
98
106
|
.action(async (opts) => {
|
|
99
|
-
verboseOf(opts)("mcp", "starting MCP server on stdio", "
|
|
107
|
+
verboseOf(opts)("mcp", "starting MCP server on stdio", "notice");
|
|
100
108
|
await serveMcp(verboseOf(opts, "mcp"));
|
|
101
109
|
});
|
|
102
110
|
/* the init command creates the configured artifact files */
|
|
@@ -116,13 +124,13 @@ withCommonOptions(program.command("lint"))
|
|
|
116
124
|
const specbook = new SpecBook({ verbose: verboseOf(opts) });
|
|
117
125
|
const result = await specbook.lint({ config: configOf(opts), basedir: opts.basedir });
|
|
118
126
|
for (const diagnostic of result.diagnostics)
|
|
119
|
-
await writeStdout(opts.verbose ?
|
|
127
|
+
await writeStdout(parseVerbosity(opts.verbose) > 0 ?
|
|
120
128
|
renderDiagnosticVerbose(diagnostic, process.stdout.isTTY === true) :
|
|
121
129
|
`${renderDiagnostic(diagnostic)}\n`);
|
|
122
130
|
if (result.diagnostics.some((diagnostic) => diagnostic.severity === "error"))
|
|
123
131
|
process.exitCode = 1;
|
|
124
132
|
else
|
|
125
|
-
verboseOf(opts)("lint", "specification valid", "
|
|
133
|
+
verboseOf(opts)("lint", "specification valid", "notice");
|
|
126
134
|
});
|
|
127
135
|
/* the export command parses the input once and writes every output */
|
|
128
136
|
withCommonOptions(program.command("export"))
|
|
@@ -72,7 +72,7 @@ export const watchSpecification = async (run, verbose) => {
|
|
|
72
72
|
"error" event would otherwise terminate the process */
|
|
73
73
|
watcher.on("error", (err) => {
|
|
74
74
|
verbose("observing failed: " +
|
|
75
|
-
(err instanceof Error ? err.message : String(err)), "
|
|
75
|
+
(err instanceof Error ? err.message : String(err)), "none");
|
|
76
76
|
});
|
|
77
77
|
/* perform a re-export and re-synchronize the observed files */
|
|
78
78
|
const cycle = async () => {
|
|
@@ -95,7 +95,7 @@ export const watchSpecification = async (run, verbose) => {
|
|
|
95
95
|
timer = undefined;
|
|
96
96
|
chain = chain.then(cycle).catch((err) => {
|
|
97
97
|
verbose("re-export failed: " +
|
|
98
|
-
(err instanceof Error ? err.message : String(err)), "
|
|
98
|
+
(err instanceof Error ? err.message : String(err)), "none");
|
|
99
99
|
});
|
|
100
100
|
}, watchDelay);
|
|
101
101
|
});
|
package/dst/specbook-cmd-lint.js
CHANGED
|
@@ -75,9 +75,10 @@ export const lint = (options) => {
|
|
|
75
75
|
options.verbose(`parsed specification defining ${literal(stats.objects)} object(s) ` +
|
|
76
76
|
`and ${literal(stats.links)} link relationship(s)`);
|
|
77
77
|
/* report the reference coverage the "referenced"-flagged object
|
|
78
|
-
kinds receive and the "coverage"-configured objects report
|
|
79
|
-
unreferenced objects of the latter by
|
|
80
|
-
former are already
|
|
78
|
+
kinds receive and the "coverage"-configured objects report as
|
|
79
|
+
additional figures, the unreferenced objects of the latter by
|
|
80
|
+
name as a lengthy detail (the ones of the former are already
|
|
81
|
+
reported as warnings) */
|
|
81
82
|
if (config !== undefined && result.specification.artifacts.length > 0) {
|
|
82
83
|
const index = buildLinkIndex(result.specification);
|
|
83
84
|
const schemas = collectSchemas(result.specification, config);
|
|
@@ -88,13 +89,17 @@ export const lint = (options) => {
|
|
|
88
89
|
const chain = chainOf(index, covered[0] ?? uncovered[0]);
|
|
89
90
|
options.verbose((schema.referenced ?? []).map((entry) => `"${literal(entry)}"`).join(" or ") +
|
|
90
91
|
` references ${ratio(covered, uncovered)} ${schema.kind} object(s)` +
|
|
91
|
-
(chain.length > 1 ? ` below ${label(chain[0])}` : ""));
|
|
92
|
+
(chain.length > 1 ? ` below ${label(chain[0])}` : ""), "detail");
|
|
92
93
|
}
|
|
93
94
|
for (const [object, entries] of specCoverage(index, schemas))
|
|
94
|
-
for (const { pattern, covered, uncovered } of entries)
|
|
95
|
+
for (const { pattern, covered, uncovered } of entries) {
|
|
95
96
|
options.verbose(`${label(object)} references ${ratio(covered, uncovered)} ` +
|
|
96
|
-
`"${literal(pattern)}" object(s)
|
|
97
|
-
|
|
97
|
+
`"${literal(pattern)}" object(s)`, "detail");
|
|
98
|
+
if (uncovered.length > 0)
|
|
99
|
+
options.verbose(`${label(object)} leaves ${literal(uncovered.length)} ` +
|
|
100
|
+
`"${literal(pattern)}" object(s) unreferenced: ` +
|
|
101
|
+
uncovered.map(label).join(", "), "trace");
|
|
102
|
+
}
|
|
98
103
|
}
|
|
99
104
|
/* report the non-optional artifacts absent from the specification,
|
|
100
105
|
against their loaded artifact file (an absent or unreadable file
|
|
@@ -36,16 +36,16 @@ export const servePreview = async (options) => {
|
|
|
36
36
|
/* identify the client by its remote address and port */
|
|
37
37
|
const addr = request.socket.remoteAddress ?? "unknown";
|
|
38
38
|
const client = `${addr.includes(":") ? `[${addr}]` : addr}:${request.socket.remotePort ?? 0}`;
|
|
39
|
-
options.verbose(`client ${literal(client)} connected`, "
|
|
39
|
+
options.verbose(`client ${literal(client)} connected`, "none");
|
|
40
40
|
clients.add(socket);
|
|
41
41
|
socket.on("close", () => {
|
|
42
42
|
clients.delete(socket);
|
|
43
|
-
options.verbose(`client ${literal(client)} disconnected`, "
|
|
43
|
+
options.verbose(`client ${literal(client)} disconnected`, "none");
|
|
44
44
|
});
|
|
45
45
|
/* report a failing client, as an unhandled "error" event
|
|
46
46
|
would otherwise terminate the process */
|
|
47
47
|
socket.on("error", (err) => {
|
|
48
|
-
options.verbose(`client ${literal(client)} failed: ${err.message}`, "
|
|
48
|
+
options.verbose(`client ${literal(client)} failed: ${err.message}`, "none");
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
});
|
|
@@ -53,12 +53,12 @@ export const servePreview = async (options) => {
|
|
|
53
53
|
await fastify.listen({ host: options.addr, port: options.port });
|
|
54
54
|
const host = options.addr.includes(":") ? `[${options.addr}]` : options.addr;
|
|
55
55
|
const url = `http://${host}:${options.port}/`;
|
|
56
|
-
options.verbose(`listening on ${literal(url)}`, "
|
|
56
|
+
options.verbose(`listening on ${literal(url)}`, "none");
|
|
57
57
|
return {
|
|
58
58
|
update: (buffer) => {
|
|
59
59
|
/* the first export turns the 503 placeholder into the document */
|
|
60
60
|
if (html === undefined)
|
|
61
|
-
options.verbose(`serving ${literal(url)}`, "
|
|
61
|
+
options.verbose(`serving ${literal(url)}`, "none");
|
|
62
62
|
html = buffer;
|
|
63
63
|
options.verbose(`notifying ${literal(clients.size)} preview client(s)`);
|
|
64
64
|
for (const client of clients)
|
|
@@ -176,7 +176,7 @@ const resolveBrowser = async (verbose) => {
|
|
|
176
176
|
const chrome = await chromium.launch({ channel: "chrome" });
|
|
177
177
|
await chrome.close();
|
|
178
178
|
verbose("Playwright Chromium unavailable -- falling back to the " +
|
|
179
|
-
`system-installed Google Chrome (${chromiumRemedy})`, "
|
|
179
|
+
`system-installed Google Chrome (${chromiumRemedy})`, "none");
|
|
180
180
|
return { channel: "chrome" };
|
|
181
181
|
}
|
|
182
182
|
catch {
|
|
@@ -248,7 +248,7 @@ export const htmlToPdf = async (renderHtmlPass, heading, outline, titlePage, ver
|
|
|
248
248
|
would otherwise ship silently wrong page numbers */
|
|
249
249
|
if (!stable)
|
|
250
250
|
verbose(`the ToC page numbers did not stabilize within ${literal(tocPasses)} ` +
|
|
251
|
-
"passes -- they can be off by a page", "
|
|
251
|
+
"passes -- they can be off by a page", "none");
|
|
252
252
|
/* render the final document, decorated with header/footer */
|
|
253
253
|
const decorated = await renderPdf(html, {
|
|
254
254
|
displayHeaderFooter: true,
|
package/dst/specbook-format.md
CHANGED
|
@@ -622,7 +622,8 @@ the description of the reporting object (one row per pattern, labeled
|
|
|
622
622
|
by the kinds of the matching objects, with the covered and total counts
|
|
623
623
|
and the ratio as a bar), the AST exports attach the counts as the
|
|
624
624
|
`coverage` field of the object, and the verbose output of `lint` and
|
|
625
|
-
`export`
|
|
625
|
+
`export` states the ratio at its higher levels and names the
|
|
626
|
+
unreferenced objects at its highest one.
|
|
626
627
|
|
|
627
628
|
### Format
|
|
628
629
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
export type VerboseLevel = "
|
|
1
|
+
export type VerboseLevel = "none" | "notice" | "detail" | "trace";
|
|
2
2
|
export type Verbose = (msg: string, level?: VerboseLevel) => void;
|
|
3
|
+
export declare const verbosities: readonly [0, 1, 2, 3];
|
|
4
|
+
export type Verbosity = typeof verbosities[number];
|
|
5
|
+
export declare const verbosityOf: Record<VerboseLevel, Verbosity>;
|
|
6
|
+
export declare const parseVerbosity: (value: string | number | boolean) => Verbosity;
|
|
3
7
|
export declare const literal: (value: string | number) => string;
|
|
4
8
|
export declare const renderVerbose: (msg: string, style?: (value: string) => string) => string;
|
package/dst/specbook-verbose.js
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
** Copyright (c) 2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
4
|
** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
|
|
5
5
|
*/
|
|
6
|
+
/* the verbosity a consumer runs at, as the least one surfacing the
|
|
7
|
+
messages of each level: a message passes a consumer whose verbosity
|
|
8
|
+
is at least the one of its level */
|
|
9
|
+
export const verbosities = [0, 1, 2, 3];
|
|
10
|
+
export const verbosityOf = { none: 0, notice: 1, detail: 2, trace: 3 };
|
|
11
|
+
/* parse and validate a verbosity specification, where a bare flag (or
|
|
12
|
+
a boolean word) selects the regular processing information */
|
|
13
|
+
export const parseVerbosity = (value) => {
|
|
14
|
+
let level;
|
|
15
|
+
if (typeof value === "boolean")
|
|
16
|
+
level = value ? 1 : 0;
|
|
17
|
+
else if (typeof value === "string" && (/^(?:true|yes|on)$/i).test(value))
|
|
18
|
+
level = 1;
|
|
19
|
+
else if (typeof value === "string" && (/^(?:false|no|off)$/i).test(value))
|
|
20
|
+
level = 0;
|
|
21
|
+
else
|
|
22
|
+
level = typeof value === "string" && !(/^\d+$/).test(value) ? NaN : Number(value);
|
|
23
|
+
if (!verbosities.includes(level))
|
|
24
|
+
throw new Error(`unknown verbosity "${value}" ` +
|
|
25
|
+
`(supported: ${verbosities.join(", ")})`);
|
|
26
|
+
return level;
|
|
27
|
+
};
|
|
6
28
|
/* the sentinel characters delimiting a marked literal value, taken
|
|
7
29
|
from the Unicode private use area, so they never clash with the
|
|
8
30
|
regular content of a verbose message */
|