edf2csv 0.5.86 → 0.5.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/dist/cli.d.ts +10 -1
- package/dist/cli.js +58 -6
- package/dist/cli.js.map +1 -1
- package/dist/convert/run.d.ts +15 -0
- package/dist/convert/run.js +56 -36
- package/dist/convert/run.js.map +1 -1
- package/dist/edf/errors.d.ts +8 -1
- package/dist/edf/errors.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,63 @@
|
|
|
3
3
|
Notable changes to edf2csv. Versions follow [semantic versioning](https://semver.org); while the
|
|
4
4
|
major version is 0, a minor bump may contain breaking changes.
|
|
5
5
|
|
|
6
|
+
## 0.5.88
|
|
7
|
+
|
|
8
|
+
### Fixed: a worker killed from outside made the batch exit 2, "the command line is the problem"
|
|
9
|
+
|
|
10
|
+
`worstOf` combines the children's exit codes into the run's:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
return codes.includes(EXIT_ERROR) ? EXIT_ERROR : EXIT_USAGE;
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
which assumes every child exits 1 or 2. A child killed by a signal exits 130 or 143 — its own
|
|
17
|
+
interrupt handler does that — so a `--jobs` worker stopped by SIGTERM fell through to 2. The
|
|
18
|
+
exit-code table calls 2 "The command line is the problem", and warnings-and-errors "The command
|
|
19
|
+
was invoked incorrectly, or asked for something the recording can't provide". The command was
|
|
20
|
+
fine; something killed a worker, which is how the out-of-memory killer and a scheduler's time
|
|
21
|
+
limit both arrive. A script branching on 2 to mean "I typed it wrong" would retry forever.
|
|
22
|
+
|
|
23
|
+
Asked the other way round now: 2 is the narrow claim, so every recording has to have earned it,
|
|
24
|
+
and anything else in the list makes the run a failure. `[143]` is 1, `[2]` is still 2, `[2, 143]`
|
|
25
|
+
is 1.
|
|
26
|
+
|
|
27
|
+
Pinned by testing the mapping directly rather than by racing a signal at a real worker — the
|
|
28
|
+
mapping is the defect, and a batch long enough to catch mid-flight is a race in a test suite.
|
|
29
|
+
|
|
30
|
+
## 0.5.87
|
|
31
|
+
|
|
32
|
+
### Fixed: `--info --stdout` described files the command never writes
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
$ edf2csv mixed-rates.edf --info --stdout
|
|
36
|
+
Would write 1,155 rows, roughly 22.2 KB.
|
|
37
|
+
warning: Channels use 3 different sampling rates (256 Hz, 128 Hz, 1 Hz).
|
|
38
|
+
They are written to one file per rate so no channel is resampled.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
for a command that refuses to run, writes nothing, and names no file:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
$ edf2csv mixed-rates.edf --stdout
|
|
45
|
+
error: --stdout needs exactly one table, but this recording produces 3 ...
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`--info` exists to say what a conversion will do, and refusing is one of the things it does.
|
|
49
|
+
It was not passed `--stdout` at all, so the plan it described was a different command's.
|
|
50
|
+
|
|
51
|
+
The conversion's three `--stdout` guards are lifted into `stdoutRefusal` and `--info` asks the
|
|
52
|
+
same question, so there is one wording rather than two that can drift — the test asserts the
|
|
53
|
+
preview contains the refusal's own sentence. Reported as a warning rather than a refusal, for
|
|
54
|
+
the reason 0.5.51 gives about the destination guards: `--info` writes nothing, so a rule about
|
|
55
|
+
the output has no business stopping it from describing the recording, and being told the command
|
|
56
|
+
will not work is exactly what was asked. New diagnostic code `STDOUT_UNSUPPORTED`, documented on
|
|
57
|
+
all three pages.
|
|
58
|
+
|
|
59
|
+
`--info --stdout --json` is no longer refused either. That guard exists because a CSV and a
|
|
60
|
+
summary cannot share stdout; under `--info` there is no CSV, and the description *is* the JSON.
|
|
61
|
+
It was the one way a script could see this warning at all.
|
|
62
|
+
|
|
6
63
|
## 0.5.86
|
|
7
64
|
|
|
8
65
|
### Fixed: a file holding half a million samples was told no data was written
|
package/dist/cli.d.ts
CHANGED
|
@@ -9,4 +9,13 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { defaultOutputDir } from './convert/run.js';
|
|
11
11
|
export declare function main(argv: readonly string[]): Promise<number>;
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The exit code for a run in which several files failed for different reasons.
|
|
14
|
+
*
|
|
15
|
+
* 2 means "you invoked it wrong" and 1 means "something went wrong with a file". When both
|
|
16
|
+
* happened, 1 is the honest answer: the invocation cannot be the whole story once a file has
|
|
17
|
+
* genuinely failed. With a single input there is only ever one code, so its exit status is
|
|
18
|
+
* exactly what it always was.
|
|
19
|
+
*/
|
|
20
|
+
declare function worstOf(codes: readonly number[]): number;
|
|
21
|
+
export { defaultOutputDir, worstOf };
|
package/dist/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ import process from 'node:process';
|
|
|
18
18
|
import { EdfError } from './edf/errors.js';
|
|
19
19
|
import { EdfFile } from './edf/reader.js';
|
|
20
20
|
import { buildPlan, withoutFileRateWarning } from './convert/plan.js';
|
|
21
|
-
import { ConversionError, USAGE_ERROR_CODES, convert, defaultOutputDir } from './convert/run.js';
|
|
21
|
+
import { ConversionError, USAGE_ERROR_CODES, convert, defaultOutputDir, stdoutRefusal, } from './convert/run.js';
|
|
22
22
|
import { ChannelSelectionError } from './convert/channels.js';
|
|
23
23
|
// Shared with the library so a bad option is the same error whichever way it arrived.
|
|
24
24
|
import { OptionError } from './convert/options.js';
|
|
@@ -256,7 +256,17 @@ export async function main(argv) {
|
|
|
256
256
|
// Both of these claim stdout. Allowing them together wrote the CSV and then the summary
|
|
257
257
|
// object onto one stream, producing a document that is neither valid CSV nor valid
|
|
258
258
|
// JSON — and silently, since each half looked right on its own.
|
|
259
|
-
|
|
259
|
+
/*
|
|
260
|
+
Not under --info, which writes no CSV for --json to collide with.
|
|
261
|
+
|
|
262
|
+
The two claim stdout because one puts a CSV there and the other a summary. `--info` puts
|
|
263
|
+
neither: it prints a description, and under --json that description *is* the JSON. So
|
|
264
|
+
`--info --stdout --json` — a script asking "would --stdout work on this recording, in a
|
|
265
|
+
form I can parse" — was refused for a conflict that cannot arise, which is the same shape
|
|
266
|
+
as the destination guards 0.5.51 stopped applying to it. It is also the only way to see
|
|
267
|
+
the STDOUT_UNSUPPORTED warning from a script.
|
|
268
|
+
*/
|
|
269
|
+
if (toStdout && asJson && values['info'] !== true) {
|
|
260
270
|
/*
|
|
261
271
|
`error: ` and the seven-space continuation, like every other refusal.
|
|
262
272
|
|
|
@@ -409,7 +419,7 @@ export async function main(argv) {
|
|
|
409
419
|
if (batch && !asJson && index > 0)
|
|
410
420
|
process.stdout.write('\n');
|
|
411
421
|
try {
|
|
412
|
-
warnings += await showInfo(input, shared, asJson, jsonIndent, batch);
|
|
422
|
+
warnings += await showInfo(input, shared, asJson, jsonIndent, batch, values['stdout'] === true);
|
|
413
423
|
}
|
|
414
424
|
catch (error) {
|
|
415
425
|
failures.push(reportError(error, batch ? input : undefined));
|
|
@@ -618,7 +628,7 @@ export async function main(argv) {
|
|
|
618
628
|
}
|
|
619
629
|
}
|
|
620
630
|
/** Print one recording's `--info`, and return how many diagnostics it raised. */
|
|
621
|
-
async function showInfo(input, shared, asJson, jsonIndent, batch = false) {
|
|
631
|
+
async function showInfo(input, shared, asJson, jsonIndent, batch = false, toStdout = false) {
|
|
622
632
|
const file = await EdfFile.open(input);
|
|
623
633
|
try {
|
|
624
634
|
/*
|
|
@@ -664,6 +674,30 @@ async function showInfo(input, shared, asJson, jsonIndent, batch = false) {
|
|
|
664
674
|
recordStarts: timing.starts,
|
|
665
675
|
}, shared);
|
|
666
676
|
plan.diagnostics.push(...timing.diagnostics);
|
|
677
|
+
/*
|
|
678
|
+
What --stdout would do with this recording, which --info did not ask.
|
|
679
|
+
|
|
680
|
+
`--info --stdout` on a three-rate file predicted "Would write 1,155 rows, roughly
|
|
681
|
+
22.2 KB" and said the channels "are written to one file per rate" — for a command that
|
|
682
|
+
refuses to run, writes nothing and names no file. --info exists to say what a
|
|
683
|
+
conversion will do, and refusing is one of the things it does.
|
|
684
|
+
|
|
685
|
+
A warning rather than a refusal, for the reason 0.5.51 gives about the destination
|
|
686
|
+
guards: --info writes nothing, so a rule about the output has no business stopping it
|
|
687
|
+
from describing the recording — and being told the command will not work is exactly
|
|
688
|
+
what was asked. The conversion's own guard supplies the words, so there is one wording.
|
|
689
|
+
*/
|
|
690
|
+
if (toStdout) {
|
|
691
|
+
const refusal = stdoutRefusal(file, plan);
|
|
692
|
+
if (refusal) {
|
|
693
|
+
plan.diagnostics.push({
|
|
694
|
+
code: 'STDOUT_UNSUPPORTED',
|
|
695
|
+
severity: 'warning',
|
|
696
|
+
message: `--stdout would refuse this recording: ${refusal.message.replace(/^--stdout /u, '')}`,
|
|
697
|
+
...(refusal.hint === undefined ? {} : { hint: refusal.hint }),
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
}
|
|
667
701
|
process.stdout.write(asJson ? `${infoJson(file, plan, jsonIndent)}\n` : `${formatInfo(file, plan)}\n`);
|
|
668
702
|
// Under --json the warnings travel inside the document, exactly as they do for a
|
|
669
703
|
// conversion, so stderr stays empty and the whole result is one parseable thing.
|
|
@@ -1254,7 +1288,22 @@ function assertDistinct(inputs, destinations) {
|
|
|
1254
1288
|
* exactly what it always was.
|
|
1255
1289
|
*/
|
|
1256
1290
|
function worstOf(codes) {
|
|
1257
|
-
|
|
1291
|
+
/*
|
|
1292
|
+
Anything that is not "the command line is the problem" is a failure of the run.
|
|
1293
|
+
|
|
1294
|
+
This tested for `EXIT_ERROR` and fell through to `EXIT_USAGE` for everything else, on the
|
|
1295
|
+
assumption that a child exits 1 or 2. A child killed by a signal exits 130 or 143 — its
|
|
1296
|
+
own interrupt handler does that — so a `--jobs` worker stopped by SIGTERM made the batch
|
|
1297
|
+
exit 2, which cli-reference's table defines as "The command line is the problem" and
|
|
1298
|
+
warnings-and-errors as "The command was invoked incorrectly, or asked for something the
|
|
1299
|
+
recording can't provide". The command was fine; something killed a worker. The
|
|
1300
|
+
out-of-memory killer and a scheduler's time limit both arrive exactly this way, and the
|
|
1301
|
+
serial path reports the same event as the failure it is.
|
|
1302
|
+
|
|
1303
|
+
Asking the question the other way round is also the honest one: 2 is the narrow claim,
|
|
1304
|
+
so it should be the one that has to be earned by every code in the list.
|
|
1305
|
+
*/
|
|
1306
|
+
return codes.some((code) => code !== EXIT_USAGE) ? EXIT_ERROR : EXIT_USAGE;
|
|
1258
1307
|
}
|
|
1259
1308
|
/**
|
|
1260
1309
|
* Convert one recording in a separate process, and collect everything it printed.
|
|
@@ -1567,7 +1616,10 @@ function message(error, indent = '') {
|
|
|
1567
1616
|
// one, alongside the usage text, so they pass nothing and stay flush left.
|
|
1568
1617
|
return printableLines(error instanceof Error ? error.message : String(error), indent);
|
|
1569
1618
|
}
|
|
1570
|
-
|
|
1619
|
+
// `worstOf` is exported for the test that pins the exit-code mapping. A batch's verdict is
|
|
1620
|
+
// one of the few things a script branches on, and the mapping is easier to check directly
|
|
1621
|
+
// than by racing a signal at a worker.
|
|
1622
|
+
export { defaultOutputDir, worstOf };
|
|
1571
1623
|
/**
|
|
1572
1624
|
* Whether this file was executed rather than imported.
|
|
1573
1625
|
*
|