edf2csv 0.7.176 → 0.7.177
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/cli.js +18 -1
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -561,7 +561,24 @@ export async function main(argv) {
|
|
|
561
561
|
failures.push(EXIT_ERROR);
|
|
562
562
|
if (failures.length > 0)
|
|
563
563
|
return worstOf(failures);
|
|
564
|
-
|
|
564
|
+
/*
|
|
565
|
+
And it says that --strict is why, as a conversion does.
|
|
566
|
+
|
|
567
|
+
This returned the code and printed nothing. Exit 1 out of `--info` otherwise means the
|
|
568
|
+
recording could not be read, so a screening script — the use this combination is
|
|
569
|
+
documented for, "a cheap way to screen a directory before anyone converts them" —
|
|
570
|
+
could not tell a broken file from one that merely raised a warning. Under `--json` it
|
|
571
|
+
was worse: a well-formed report on stdout, an empty stderr, and a 1.
|
|
572
|
+
|
|
573
|
+
The sentence the conversion path writes is the one that was missing, minus its closing
|
|
574
|
+
clause: there is no output to say was still written.
|
|
575
|
+
*/
|
|
576
|
+
if (strict && warnings > 0) {
|
|
577
|
+
process.stderr.write(`\n${wrap(`--strict: ${warnings} warning${warnings === 1 ? '' : 's'} raised, so this run is ` +
|
|
578
|
+
`reported as a failure. Nothing was written either way, since --info converts nothing.`)}\n`);
|
|
579
|
+
return EXIT_ERROR;
|
|
580
|
+
}
|
|
581
|
+
return EXIT_OK;
|
|
565
582
|
}
|
|
566
583
|
// The meter redraws one line in place, which two conversions cannot share. Running them
|
|
567
584
|
// at once replaces it with the completion count each file prints as it lands.
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * Command-line entry point.\n *\n * Output discipline: anything that is the *result* of a command goes to stdout\n * (`--info`'s channel table, `--json`'s summary). Progress, warnings and the\n * conversion summary go to stderr, so a conversion can be run in a pipeline without\n * its chatter contaminating the data.\n */\n\nimport { parseArgs } from 'node:util';\nimport { pathToFileURL, fileURLToPath } from 'node:url';\nimport { existsSync, realpathSync } from 'node:fs';\nimport { lstat, readdir, realpath, stat } from 'node:fs/promises';\nimport { availableParallelism } from 'node:os';\nimport { fork } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\nimport path from 'node:path';\nimport process from 'node:process';\n\nimport { EdfError } from './edf/errors.js';\nimport { EdfFile } from './edf/reader.js';\nimport { buildPlan, withoutFileRateWarning } from './convert/plan.js';\nimport {\n ConversionError,\n USAGE_ERROR_CODES,\n auditStdout,\n convert,\n defaultOutputDir,\n durationDiagnostics,\n requestedAnnotationWindow,\n noAnnotations,\n noSignalFile,\n stdoutRefusal,\n} from './convert/run.js';\nimport type { ConvertOptions } from './convert/run.js';\nimport { ChannelSelectionError } from './convert/channels.js';\n// Shared with the library so a bad option is the same error whichever way it arrived.\nimport { OptionError } from './convert/options.js';\nimport { TimeRangeError, parseTimeSpec } from './convert/time-range.js';\nimport { deriveRecordStarts, withTimingPromiseKept } from './convert/timing.js';\nimport { formatDiagnostics, formatInfo, infoJson, formatSummary, printable, printableLines, summaryJson, wrap } from './cli/report.js';\nimport { counted, listed } from './format/list.js';\nimport { VERSION } from './version.js';\n\nconst USAGE = `edf2csv ${VERSION}\nConvert EDF, EDF+ and BDF recordings to CSV\n\nUsage\n edf2csv <recording.edf | folder> [more ...] [options]\n\nOptions\n -i, --info Show the recording's structure and the estimated\n output size, without converting anything\n -o, --out <dir> Output directory (default: <recording>_csv, beside\n the input)\n -c, --channels <list> Only these channels, comma-separated. Use #N to pick a\n channel by position when two share a label\n --start <time> Begin at this offset (30s, 5m, 1h30m, 00:30:00)\n --duration <time> Convert this much\n --end <time> Stop at this offset (instead of --duration)\n --annotations-only Write only the EDF+ annotations, no signal data\n --decimals <n> Fix the decimal places instead of deriving them\n per channel\n --checksum Record a SHA-256 of the input in metadata.json\n --layout <kind> wide (default): one column per channel, one file per\n sampling rate. long: one file of time_s,channel,value,\n every rate together, one row per sample\n --gzip Compress every CSV, writing .csv.gz files\n --bom Start each CSV with a UTF-8 byte order mark, so Excel\n reads accented text and units like µV correctly\n -j, --jobs <n> Convert this many recordings at once, or \"auto\"\n (default: 1)\n -f, --force Write into the output directory if it already exists\n -q, --quiet Suppress the summary; warnings and errors still print\n --json Print machine-readable JSON to stdout (works with\n --info too)\n --strict Exit 1 if the recording raised any warning\n --stdout Write the signal CSV to stdout instead of a directory\n (one table only: one sampling rate, or --layout long)\n -h, --help Show this help\n -V, --version Show the version\n\nSeveral recordings\n A folder is expanded to every .edf and .bdf inside it, at any depth, and the\n layout is kept: recordings in sub-folders come out in sub-folders.\n Pass more than one and each is converted in turn. Without --out each lands\n beside itself as usual; with --out that directory becomes the parent and each\n recording gets its own inside it. A file that cannot be read is reported and\n the rest still convert, with a non-zero exit at the end. --jobs converts\n several at once, which is worth it for a folder of them.\n\nOutput\n A directory containing signals.csv, channels.csv, metadata.json, and\n annotations.csv when the recording carries EDF+ annotations. Channels recorded\n at different sampling rates are written to separate files, never resampled.\n With --gzip each CSV becomes a .csv.gz; metadata.json stays plain text so the\n directory can still be read at a glance. With --bom each CSV starts with a\n UTF-8 byte order mark and metadata.json does not, since JSON.parse rejects\n one.\n With --layout long every channel goes into one signals.csv as time_s, channel\n and value, one row per sample, whatever rates the recording mixes — which is\n also the one arrangement --stdout can stream for a mixed-rate file.\n\nExamples\n edf2csv recording.edf\n edf2csv recording.edf --info\n edf2csv recording.edf --channels \"EEG Fpz-Cz,ECG\" --out ./converted\n edf2csv recording.edf --start 30m --duration 5m\n edf2csv recording.edf --annotations-only\n edf2csv /data/*.edf --out ./converted\n edf2csv /data/*.edf --out ./converted --jobs auto\n edf2csv /data/study --out ./converted --jobs auto\n`;\n\n/**\n * A continuation line under an `error: ` or `interrupted (SIGINT): ` head.\n *\n * These are written straight to stderr rather than raised as an error, so they never passed\n * through `printableLines` and never wrapped. Most of them interpolate a destination or a\n * recording path, which makes the line as long as the caller's directory tree is deep: the\n * `--stdout` refusal for a folder reached 268 columns on a four-deep path, saying \"Name the\n * recording itself — <path> — or convert to a directory instead.\"\n *\n * A path has no spaces to break at, so it stays whole on its own line and the sentence\n * around it wraps. That is the useful shape: the path is the part that gets copied.\n */\nconst detail = (text: string): string => `${wrap(text, ' ')}\\n`;\n\n/**\n * Every option the command line takes.\n *\n * Its own constant so `parseArgs` and the \"did you mean\" suggestion read the same list. A\n * second copy of twenty flag names is a copy that will be missing the next one.\n */\nconst OPTIONS = {\n info: { type: 'boolean', short: 'i' },\n out: { type: 'string', short: 'o' },\n channels: { type: 'string', short: 'c', multiple: true },\n start: { type: 'string' },\n duration: { type: 'string' },\n end: { type: 'string' },\n 'annotations-only': { type: 'boolean' },\n decimals: { type: 'string' },\n checksum: { type: 'boolean' },\n layout: { type: 'string' },\n gzip: { type: 'boolean' },\n bom: { type: 'boolean' },\n jobs: { type: 'string', short: 'j' },\n force: { type: 'boolean', short: 'f' },\n quiet: { type: 'boolean', short: 'q' },\n json: { type: 'boolean' },\n strict: { type: 'boolean' },\n stdout: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'V' },\n} as const;\n\nconst EXIT_OK = 0;\nconst EXIT_ERROR = 1;\nconst EXIT_USAGE = 2;\n\n/**\n * Whether this run is already shutting its children down.\n *\n * A child dying by signal is normally worth a line of its own, but not when this process is\n * the one that killed it: the interrupt handler names every abandoned directory in a single\n * message, and a per-child line underneath it would say the same thing again, once per job.\n */\nlet stopping = false;\n\n/**\n * Piping into a consumer that exits early (`| head -1`) closes our stdout, and the\n * next write raises EPIPE. That is normal in a shell pipeline, not an error worth a\n * stack trace, so it is swallowed while anything else still surfaces.\n */\nfunction ignoreBrokenPipe(stream: NodeJS.WriteStream): void {\n stream.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EPIPE') {\n // A reader closing early (`edf2csv ... --info | head -5`) is not a failure, so the\n // error is swallowed rather than thrown. It deliberately does NOT set an exit code:\n // forcing 0 here would erase a real failure whenever the pipe happened to close\n // after the run had already failed, reporting success for a conversion that did\n // not happen. Node exits 0 on its own when nothing sets a code.\n return;\n }\n\n /*\n Anything else is a real write failure, and this used to rethrow it.\n\n The throw lands on a nextTick, outside whatever try/catch the conversion is running\n inside, so it became an uncaught exception: `--stdout` redirected onto a full disk\n died with a raw stack trace headed `dist/cli.js: throw error;` and lost the warning\n that the CSV it had already produced was truncated. The same failure through `--out`\n printed the ordinary message, and so did `--stdout` through the library API, which\n never registers this listener — the designed path exists and works, and this was\n preempting it.\n\n A second listener means the conversion's writer is watching this stream and will\n turn the failure into that ordinary message. Saying it here too would report one\n failure twice, so this only speaks when nothing else will.\n */\n if (stream.listenerCount('error') > 1) return;\n process.stderr.write(`error: Writing to ${stream === process.stdout ? 'stdout' : 'stderr'} failed: ${error.message}\\n`);\n process.exitCode = EXIT_ERROR;\n });\n}\n\nexport async function main(argv: readonly string[]): Promise<number> {\n ignoreBrokenPipe(process.stdout);\n ignoreBrokenPipe(process.stderr);\n\n let values: Record<string, unknown>;\n let positionals: string[];\n\n try {\n const parsed = parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: OPTIONS,\n });\n values = parsed.values as Record<string, unknown>;\n positionals = parsed.positionals;\n } catch (error) {\n process.stderr.write(`${usageMessage(error, argv)}\\n\\nRun edf2csv --help to see the options.\\n`);\n return EXIT_USAGE;\n }\n\n if (values['help'] === true) {\n process.stdout.write(USAGE);\n return EXIT_OK;\n }\n if (values['version'] === true) {\n process.stdout.write(`${VERSION}\\n`);\n return EXIT_OK;\n }\n\n if (positionals.length === 0) {\n process.stderr.write(`No input file given.\\n\\n${USAGE}`);\n return EXIT_USAGE;\n }\n\n /*\n An empty --out, refused here rather than by the filesystem.\n\n Every other option that takes a value refuses an empty one from the command line and says\n what it wanted: `--start is empty`, `--channels was given but lists no channel names`,\n `--layout must be \"wide\" or \"long\", got \"\"`. `--out \"\"` was the eighth of eight and the\n exception — it went the whole way to `mkdir('')` and came back as a conversion failure\n about a path:\n\n error: Cannot create \"\": part of the path does not exist.\n Check the path exists and that you have permission to write there.\n\n Exit 1 where the other seven are exit 2, advice about a path and a permission for a value\n that is neither, and in a batch it is decided once per recording rather than once for the\n run. `--out \"$DEST\"` with `DEST` unset is how it gets written by accident, which is also\n why the empty string alone is refused and the value is not trimmed first: a directory whose\n name is a space is a strange thing to ask for, but it is a thing the filesystem has and a\n path is not a keyword.\n */\n if (values['out'] === '') {\n process.stderr.write('error: --out is empty. Give a directory, for example --out ./converted.\\n');\n return EXIT_USAGE;\n }\n\n let expanded: Input[];\n let unreadable: string[];\n let namedDirectory: boolean;\n try {\n const found = await expandInputs(positionals);\n expanded = found.inputs;\n unreadable = found.unreadable;\n namedDirectory = found.namedDirectory;\n } catch (error) {\n return reportError(error);\n }\n\n // Reported before anything is converted, so it cannot be lost among the summaries, and\n // counted against the run so the exit code does not call a partial sweep a success.\n for (const entry of unreadable) {\n process.stderr.write(\n `error: ${printable(entry)}: could not be read, so any recordings inside it were skipped.\\n`,\n );\n }\n\n if (expanded.length === 0) {\n /*\n \"None here\" and \"could not look\" are different answers, and so are their exit codes.\n\n A folder the process cannot read gave the same exit 2 and the same \"No EDF or BDF\n recordings found\" as an empty one — while the line above it said the folder could not\n be read. Exit 2 is this tool's code for \"the command itself was wrong\", so a script\n was being told to fix its arguments when what needed fixing was a permission. The\n command was fine; the filesystem refused.\n\n And the sentence itself claimed a fact the run is in no position to state: nothing was\n found because nothing was looked at.\n */\n if (unreadable.length > 0) {\n process.stderr.write(\n `${wrap(\n `Nothing could be converted: ${unreadable.length === 1 ? 'that path' : 'those paths'} ` +\n `could not be read, so whether ${unreadable.length === 1 ? 'it holds' : 'they hold'} ` +\n `recordings is unknown.`,\n )}\\n`,\n );\n return EXIT_ERROR;\n }\n process.stderr.write(\n // Escaped, like every other path this prints. These come from the command line, which\n // makes them no safer: a shell glob expands to whatever the directory holds.\n `No EDF or BDF recordings found in ${listed(positionals.map((p) => `\"${printable(p)}\"`))}.\\n`,\n );\n return EXIT_USAGE;\n }\n const inputs = expanded.map((entry) => entry.path);\n /*\n A batch is what you asked for, not what happened to be there.\n\n Counting the recordings made the shape of the run depend on the contents of a folder.\n Under --json a study holding one night printed a pretty-printed object and the same study\n holding two printed two compact lines, so a script written against one of them broke on\n the other — and it broke the day a recording was added, not the day the script changed.\n An input going missing did it in reverse. This is the same count 0.4.20 took out of\n `--out` for the same reason.\n\n Counted from what was named, not from what survived deduplication. `edf2csv one.edf\n alias.edf --out o`, where `alias.edf` is a link to `one.edf`, is two names for one\n recording — converted once, which is right — and the count collapsing to 1 made the run\n stop being a batch: `--out` became the output directory itself, so the files landed in\n `o/` rather than `o/one/`, and `--json` printed one indented document where two\n recordings would have given JSON Lines. cli-reference says of exactly that command that\n it writes `out/one`.\n */\n const batch = positionals.length > 1 || namedDirectory;\n const quiet = values['quiet'] === true;\n const asJson = values['json'] === true;\n const strict = values['strict'] === true;\n const toStdout = values['stdout'] === true;\n\n // Both of these claim stdout. Allowing them together wrote the CSV and then the summary\n // object onto one stream, producing a document that is neither valid CSV nor valid\n // JSON — and silently, since each half looked right on its own.\n /*\n Not under --info, which writes no CSV for --json to collide with.\n\n The two claim stdout because one puts a CSV there and the other a summary. `--info` puts\n neither: it prints a description, and under --json that description *is* the JSON. So\n `--info --stdout --json` — a script asking \"would --stdout work on this recording, in a\n form I can parse\" — was refused for a conflict that cannot arise, which is the same shape\n as the destination guards 0.5.51 stopped applying to it. It is also the only way to see\n the STDOUT_UNSUPPORTED warning from a script.\n */\n if (toStdout && asJson && values['info'] !== true) {\n /*\n `error: ` and the seven-space continuation, like every other refusal.\n\n These two were written before the prefix was, and kept their own shape: every other\n usage error in this tool prints \"error: <what>\" with the advice indented under it, the\n documentation shows them that way, and stderr is what a script greps. A refusal that\n does not match `^error:` is invisible to that grep — and this pair is the one a script\n is most likely to hit, since both flags are things a script passes rather than a person.\n */\n process.stderr.write(\n 'error: --stdout and --json both write to stdout, so they cannot be combined.\\n' +\n ' Use --stdout for the CSV, or --json for the summary.\\n',\n );\n return EXIT_USAGE;\n }\n\n /*\n One stream holds one table, for the same reason it holds one recording: concatenating\n them would give a CSV whose rows come from different files with nothing marking where\n one ends. Naming the count makes it obvious a glob was the cause — except when the count\n is one, which happens for a folder holding a single recording. That read \"--stdout writes\n a single CSV, so it cannot take 1 recordings\": ungrammatical, and wrong on its face, since\n one recording is exactly what it can take. What it cannot take is a folder, whose contents\n are not known until they are walked.\n */\n /*\n Asked of the recordings to stream, not of the run's shape.\n\n These are different questions and 0.5.40 conflated them: it made `batch` count the names\n rather than the recordings, which is right for `--out` and for `--json`, and this guard\n read `batch`. So `edf2csv one.edf one.edf --stdout` — one recording, named twice,\n converted once, perfectly streamable — was refused with the message written for a folder,\n telling the reader to \"name the recording itself\" and quoting the name they had just\n given twice.\n\n A folder is still refused however few recordings it turns out to hold, for the reason\n 0.5.5 gives: what it holds is not known until it is walked.\n */\n if (toStdout && (inputs.length > 1 || namedDirectory)) {\n // Prefixed and indented like the rest; see the --json refusal above. The recording's\n // name goes through `printable` for the reason 0.5.67 gives: a path is untrusted text,\n // and this one is read straight out of a directory the caller named.\n //\n // Quoted where a shell would not hand it back unchanged, by the same rule as the\n // --stdout --gzip hint below, whose comment sets it out: this sentence names a recording\n // to type in place of the folder, and one found inside a folder is as likely to hold a\n // space as the folder is. Unquoted it was two arguments, which --stdout then refuses for\n // being two recordings — the message one step on from the one being read.\n const only = printable(inputs[0] as string);\n process.stderr.write(\n inputs.length === 1\n ? `error: --stdout writes a single CSV, and a folder is converted as a batch even ` +\n `when it holds one recording.\\n` +\n detail(\n `Name the recording itself — ${survivesBare(only) ? only : singleQuoted(only)} — ` +\n `or convert to a directory instead.`,\n )\n : `error: --stdout writes a single CSV, so it cannot take ` +\n `${counted(inputs.length, 'recording')}.\\n` +\n detail('Convert them to directories instead, or run edf2csv once per file.'),\n );\n return EXIT_USAGE;\n }\n\n /*\n Flags that --stdout has nowhere to put.\n\n Both were accepted and dropped in silence. `--out` names a directory that is never\n created, so the run looked like it had written one. `--checksum` is worse than useless:\n the hash is computed before the first record is read, which is a second full pass over\n the input, and then the only file it is ever written to — metadata.json — is not written\n at all. A recording large enough to want a checksum is large enough to notice reading it\n twice for nothing.\n\n Refusing rather than ignoring is what this tool already does for `--stdout --json` and\n `--stdout --annotations-only`.\n */\n /*\n `--force` and `--jobs` were the two left doing nothing in silence.\n\n `--force` means \"write into a directory that already exists\", and there is no directory.\n `--jobs` is deliberately not here. A job count is a property of the run rather than a\n request about the output, `--stdout` clamping it to one is documented, and a wrapper that\n passes `--jobs 4` to everything is not asking for something about this file.\n */\n for (const [flag, given] of [\n ['--out', values['out'] !== undefined],\n ['--checksum', values['checksum'] === true],\n ['--force', values['force'] === true],\n ] as const) {\n if (toStdout && given) {\n // `error:` and the seven-space continuation, like every other refusal — the shape\n // 0.5.79 gave the rest and these two were not enumerated in its test.\n //\n // One sentence on the first line, which is the other half of that shape. The break was\n // written into the middle of it — \"cannot be combined: --stdout writes no\" / \"files,\n // and --out has nothing to act on\" — so the line a log greps carried a fragment ending\n // in \"no\", and the line under it opened with \"files,\". `printableLines` leaves the\n // first line whole at whatever width it runs to, deliberately, so there was nothing to\n // pre-wrap it for.\n process.stderr.write(\n `error: --stdout and ${flag} cannot be combined: --stdout writes no files, and ` +\n `${flag} has nothing to act on.\\n` +\n detail(`Drop ${flag}, or drop --stdout and convert to a directory.`),\n );\n return EXIT_USAGE;\n }\n }\n\n /*\n Compressed bytes at a terminal.\n\n `--stdout --gzip` is a documented pair, and every place it is documented redirects it:\n `edf2csv rec.edf --stdout --gzip > signals.csv.gz`. Without the redirect it wrote the\n deflate stream to the terminal — 46 control bytes and three ESCs out of 244 for the\n smallest fixture here, which is a terminal left in whatever state those happened to\n describe, and nothing usable on the screen either way.\n\n This tool escapes every control byte out of a header before printing it, on the reasoning\n that a file should not be able to drive the reader's terminal. Doing it to them directly,\n from output it generated itself, is the same hazard with the argument for it removed.\n\n Only when stdout is a terminal. A pipe and a regular file are both `isTTY === false`, so\n the documented command is unaffected — and that is also why this cannot be answered by\n refusing the flag pair outright, the way `--stdout --json` is.\n\n gzip itself has declined to write compressed data to a terminal for thirty years, which\n is where a reader's expectation comes from.\n */\n if (toStdout && values['gzip'] === true && process.stdout.isTTY === true) {\n /*\n `<recording>` is not a placeholder a shell leaves alone.\n\n This line is printed on its own, indented, in the shape every command this tool offers\n is printed in — and 0.7.18 and 0.7.19 went through the others making sure each one can\n be pasted. Pasted, `<recording>` is a redirect: the shell looks for a file called\n `recording` and the command never runs, or one exists and edf2csv is handed a recording\n on stdin, which it does not read. Either way the answer to \"what do I type instead\"\n does not survive being typed.\n\n The name is in the invocation being refused, and exactly one of them reaches here: a\n folder and a second recording are both turned away above. Quoted by the same rule as\n the other two hints, so a path with a space in it stays one argument.\n */\n const named = printable(inputs[0] as string);\n process.stderr.write(\n 'error: --stdout --gzip would write compressed bytes straight to the terminal.\\n' +\n detail('Redirect it to a file or a pipe:') +\n ` edf2csv ${survivesBare(named) ? named : singleQuoted(named)} ` +\n `--stdout --gzip > signals.csv.gz\\n`,\n );\n return EXIT_USAGE;\n }\n\n /*\n One recording prints the document it always printed; several print one per line.\n\n Pretty-printed objects run together are still readable by a streaming JSON parser, but\n not by anything that reads a record per line — and a batch is exactly where that is\n wanted. Emitting a batch in the single-file shape would repeat 0.2.28's mistake of\n putting two documents on one stream and leaving the caller to work out the boundary.\n\n null, not undefined: the serialisers default this argument to 2, and a default parameter\n takes effect for undefined, which silently gave a batch the indentation it was supposed\n to be dropping.\n */\n const jsonIndent = batch ? null : 2;\n\n try {\n const outOption = typeof values['out'] === 'string' ? values['out'] : undefined;\n const destinations = destinationsFor(expanded, outOption, batch);\n /*\n Where the output would land is not --info's problem to refuse over. It is still\n --info's job to mention it.\n\n 0.5.32 stopped the guards refusing `--info`, and left nothing in their place — while\n the sentence it added to cli-reference says \"`--info --out` is how you would want to\n find out about them\". So the one command documented as the way to learn about a\n collision said nothing at all about it. Reported as a warning now, using the guard's own\n message so there is one wording rather than two.\n */\n if (values['info'] === true) {\n try {\n assertDistinct(inputs, destinations);\n } catch (error) {\n if (!(error instanceof OptionError)) throw error;\n process.stderr.write(`warning: ${printableLines(error.message, ' ')}\\n`);\n }\n }\n\n /*\n Where the output would land is not --info's problem, because --info has no output.\n\n Both guards refused it: `edf2csv study --info --out yy` on a folder holding `rec.edf`\n beside `rec/inner.edf` exited 2 with \"would be converted into yy/rec/inner, which is\n inside yy/rec — where rec.edf is converted\", and printed nothing about either\n recording. Two recordings whose names collide got the overwrite refusal the same way.\n Both messages assert a conversion and an overwrite that --info does not perform, and\n the identical command without --out describes both files happily.\n\n Which makes the refused command the useful one: `--info --out` is how you ask what a\n run would produce before committing to it, and a collision is exactly the thing you\n would want it to tell you about rather than refuse to look.\n */\n if (values['info'] !== true) assertDistinct(inputs, destinations);\n\n const shared = {\n channels: splitChannels(values['channels']),\n start: optionalTime(values['start'], '--start'),\n startText: typeof values['start'] === 'string' ? values['start'] : undefined,\n duration: optionalTime(values['duration'], '--duration'),\n end: optionalTime(values['end'], '--end'),\n endText: typeof values['end'] === 'string' ? values['end'] : undefined,\n decimals: optionalDecimals(values['decimals']),\n annotationsOnly: values['annotations-only'] === true,\n gzip: values['gzip'] === true,\n bom: values['bom'] === true,\n layout: optionalLayout(values['layout']),\n };\n\n // Validated before the --info branch, not inside the conversion path: a flag that\n // cannot be honoured is a usage error whatever mode it was given in, and accepting\n // \"--jobs 0\" in silence under --info is the kind of quiet that this tool avoids.\n // Parsed before it is overridden: --stdout converts one recording however many jobs\n // were asked for, but \"--stdout --jobs 0\" is still a request that cannot be honoured,\n // and accepting it in silence is the thing 0.4.2 fixed for --info.\n const requestedJobs = parseJobs(values['jobs'], inputs.length);\n /*\n Asking for several at once, of a mode that converts one.\n\n After `parseJobs`, not before it: a malformed count is a fact about the value and gets\n the message about the value, which is what the check above this one has always done and\n what its own comment insists on — \"a request that cannot be met is a usage error rather\n than something to accept in silence\". So `--stdout --jobs 0x10` still reports the `0x10`.\n\n `--jobs 1` is not refused. It asks for precisely what happens, and a script passing it\n uniformly is not asking for anything it will not get; `auto` resolves to 1 on one\n recording. What is refused is a number greater than one, which was accepted and dropped.\n */\n const jobs = toStdout ? 1 : requestedJobs;\n\n if (values['info'] === true) {\n const failures: number[] = [];\n let warnings = 0;\n for (const [index, input] of inputs.entries()) {\n // A blank line between reports, so several tables read as one document.\n if (batch && !asJson && index > 0) process.stdout.write('\\n');\n try {\n warnings += await showInfo(\n input as string,\n shared,\n asJson,\n jsonIndent,\n batch,\n values['stdout'] === true,\n );\n } catch (error) {\n failures.push(reportError(error, batch ? (input as string) : undefined));\n }\n }\n if (unreadable.length > 0) failures.push(EXIT_ERROR);\n if (failures.length > 0) return worstOf(failures);\n return strict && warnings > 0 ? EXIT_ERROR : EXIT_OK;\n }\n\n // The meter redraws one line in place, which two conversions cannot share. Running them\n // at once replaces it with the completion count each file prints as it lands.\n const showProgress =\n !quiet && !asJson && !toStdout && jobs === 1 && process.stderr.isTTY === true;\n\n const failures: number[] = [];\n let warnings = 0;\n let converted = 0;\n\n /** Convert input `index`, sending its output wherever the caller wants it. */\n const runOne = async (index: number, emit: Emit): Promise<void> => {\n const input = inputs[index] as string;\n const destination = destinations[index] as string;\n // Which recording this is, before anything it prints. Without it a batch produces a\n // stack of summaries and errors with nothing saying which file each belongs to.\n if (batch && !quiet && !asJson) {\n emit('err', `[${index + 1}/${inputs.length}] ${printable(input)}\\n`);\n }\n try {\n warnings += await convertOne(\n input,\n destination,\n {\n ...shared,\n // With one input and no --out, convert() derives the default itself, exactly as\n // it did before batches existed.\n outputDir: outOption === undefined && !batch ? undefined : destination,\n checksum: values['checksum'] === true,\n force: values['force'] === true,\n toStdout,\n quiet,\n asJson,\n showProgress,\n jsonIndent,\n batch,\n },\n emit,\n );\n converted++;\n } catch (error) {\n /*\n A batch keeps going. One unreadable recording among five hundred is a reason to\n report that file, not to abandon the ones already converted and refuse the rest.\n The exit code still reports the run as failed, and the closing line says how many\n of them made it, so nothing about the failure is quiet.\n */\n failures.push(reportError(error, batch ? input : undefined, emit));\n }\n };\n\n if (jobs === 1) {\n for (let index = 0; index < inputs.length; index++) {\n await runOne(index, writeThrough);\n // A single recording keeps the exit code it has always had rather than the batch's.\n if (!batch && failures.length > 0) return failures[0] as number;\n }\n } else {\n /*\n Real processes, not concurrent promises.\n\n Converting is almost entirely arithmetic and string building — 1.17 s of CPU for\n 1.24 s of wall clock on a 168 MB conversion — and Node runs that on one thread. An\n in-process pool was tried first and gained 6% on eight recordings, which is the\n overlap in the file reads and nothing else. Each conversion is already a whole\n command, so each one gets its own process, which is what `xargs -P` would do by hand.\n\n Workers take the next recording as they free up rather than splitting the list into\n equal shares. Recordings in a folder differ wildly in length, and a fixed split\n leaves every worker but one idle behind the longest file.\n\n Each child's output is held until it exits, so two finishing together cannot\n interleave one's summary with the other's warnings.\n */\n const running = new Map<ChildProcess, string>();\n\n /*\n Ctrl-C in a terminal reaches every process in the group, so the children would stop\n anyway; a signal sent to this process alone does not, and that is how a batch gets\n run from a script or a CI job. Interrupting one left four conversions writing\n gigabytes into a directory their owner believed abandoned, and the last thing on\n screen was a successful \"Done in 1.6s\" from whichever recording had just landed —\n the run read as if it had finished.\n */\n const onInterrupt = (signal: NodeJS.Signals): void => {\n stopping = true;\n const abandoned = [...running.values()];\n for (const child of running.keys()) child.kill('SIGTERM');\n /*\n A destination that is not there was never written to, and saying otherwise is the\n defect the serial handler was fixed for.\n\n `convert()` opens the recording, hashes it under `--checksum` and reads the whole\n annotation channel for record start times *before* it claims the directory — so\n Ctrl-C in the first seconds of a batch of large EDF+ recordings finds nothing on\n disk for any of them. This named every destination as incomplete output regardless,\n and advised distrusting directories `ls` then reported did not exist.\n\n Two answers rather than the serial path's three: whether a directory that IS there\n was there before this run is per-child state the parent does not keep, and the\n sentence that covers both is the one already printed.\n */\n const started = abandoned.filter((destination) => existsSync(destination));\n const untouched = abandoned.filter((destination) => !existsSync(destination));\n process.stderr.write(\n `\\ninterrupted (${signal}): ${abandoned.length} conversion` +\n `${abandoned.length === 1 ? '' : 's'} stopped part way through.\\n` +\n (started.length > 0\n ? detail(`Incomplete, and should not be used: ${listed(started.map(printable))}`)\n : '') +\n (untouched.length > 0\n ? detail(\n `Nothing was written to ${listed(untouched.map(printable))}: ` +\n `${untouched.length === 1 ? 'that directory was' : 'those directories were'} ` +\n `never created.`,\n )\n : ''),\n );\n process.exit(signal === 'SIGINT' ? 130 : 143);\n };\n process.once('SIGINT', onInterrupt);\n process.once('SIGTERM', onInterrupt);\n\n let next = 0;\n const worker = async (): Promise<void> => {\n for (let index = next++; index < inputs.length; index = next++) {\n const input = inputs[index] as string;\n const sink = buffered();\n if (!quiet && !asJson) {\n sink.emit('err', `[${index + 1}/${inputs.length}] ${printable(input)}\\n`);\n }\n const child = await convertInChild(\n input,\n destinations[index] as string,\n values,\n running,\n );\n // The report says what the child actually did; the exit code says whether it\n // got there. A child that converted and warned has a report and exits 0, since\n // --strict is the parent's to apply.\n if (child.report) {\n converted += child.report.converted;\n warnings += child.report.warnings;\n } else if (child.code === 0) {\n converted++;\n }\n if (child.code !== 0) failures.push(child.code);\n // The child saw one recording, so it printed the indented document a single\n // conversion prints. A batch is one object per line.\n sink.emit('out', asJson ? compactJson(child.out) : child.out);\n /*\n The child converted a single recording, so it named no file in its errors the way\n a batch does. Naming it here keeps the two paths identical to a reader and to\n anything grepping a log, where the [n/m] header may not be alongside.\n\n Warnings too, under --quiet, and for exactly the reason 0.5.49 gave for the serial\n path: the `[n/m] <path>` header is what pairs a warning with the file that raised\n it, --quiet suppresses that header, and it took the attribution with it. That fix\n keyed off the child's own `batch` flag, which a forked child does not have — it\n was handed one recording and one destination, so it believes it is a single\n conversion and says nothing. Two recordings, two warnings, no way to tell which\n raised which, and under --jobs not even a stable order to guess from.\n\n Not when the header is printed, or every warning would carry the name twice.\n */\n sink.emit('err', named(child.err, input, quiet));\n sink.flush();\n }\n };\n try {\n await Promise.all(Array.from({ length: jobs }, () => worker()));\n } finally {\n process.off('SIGINT', onInterrupt);\n process.off('SIGTERM', onInterrupt);\n }\n }\n\n /*\n A path that could not be read is a failure of this run, and the closing line has to\n count it before it prints rather than after.\n\n It was counted afterwards, so a folder holding one recording beside a sub-directory\n without read permission printed \"Converted 1 of 1 recordings.\" and exited 1. The line\n agreed with itself and with nothing else — which is the failure the walk's own comment\n says was fixed, quoting that very sentence. What was fixed then was the error line and\n the exit code; the summary went on saying everything worked.\n\n Counted separately from the recordings, because an unreadable path is not one of them:\n how many recordings it held is the thing nobody knows.\n */\n if (unreadable.length > 0) failures.push(EXIT_ERROR);\n\n if (batch && !quiet && !asJson) {\n const unread =\n unreadable.length > 0\n ? `; ${unreadable.length} path${unreadable.length === 1 ? '' : 's'} could not be read`\n : '';\n const failed = converted < inputs.length ? `; ${inputs.length - converted} failed` : '';\n process.stderr.write(\n `\\nConverted ${converted} of ${counted(inputs.length, 'recording')}${failed}${unread}.\\n`,\n );\n }\n\n /*\n A parent that forked this process needs the counts, not just an exit status.\n\n An exit code cannot separate \"converted, and raised warnings\" from \"did not convert\",\n and under --strict those are the same code. The parent read it as a failure, so a\n parallel run of two recordings — one of which merely warned — reported \"Converted 1 of\n 2 recordings; 1 failed\" for a run in which both converted, while the serial path said\n \"Converted 2 of 2\". `process.send` exists only when this process was forked with a\n channel, so nothing changes for an ordinary invocation.\n */\n process.send?.({ edf2csv: { converted, warnings } });\n\n if (failures.length > 0) return worstOf(failures);\n\n /*\n --strict turns any warning into a non-zero exit, for pipelines that would rather stop\n than proceed on a recording the tool had something to say about.\n\n The output is still written. A warning describes the recording, not a failure to\n convert it — a truncated file converts correctly for the records that are there — so\n destroying that work would be the wrong response. The exit code is the signal; what\n to do about it is the caller's decision, and they still have the files to inspect.\n */\n if (strict && warnings > 0) {\n process.stderr.write(\n `\\n${wrap(\n `--strict: ${warnings} warning${warnings === 1 ? '' : 's'} raised, so this run is ` +\n `reported as a failure. The output was still written.`,\n )}\\n`,\n );\n return EXIT_ERROR;\n }\n return EXIT_OK;\n } catch (error) {\n return reportError(error);\n }\n}\n\n/** Print one recording's `--info`, and return how many diagnostics it raised. */\nasync function showInfo(\n input: string,\n shared: Record<string, unknown>,\n asJson: boolean,\n jsonIndent: number | null,\n batch = false,\n toStdout = false,\n): Promise<number> {\n const file = await EdfFile.open(input);\n try {\n /*\n Read the annotation channel only when the timing actually depends on it.\n\n --info is documented as a header-only summary that returns immediately whatever\n the file's size, but it called readAnnotations() on every EDF+/BDF+ file — a seek\n into every data record. deriveRecordStarts discards that data unless the file is\n EDF+D, where record start times are stored rather than arithmetic, so on a\n continuous recording the whole scan was thrown away. It cost 0.29 s on a 12 MB\n file and scaled with record count.\n\n A discontinuous file still needs the scan: without it the reported span and row\n estimate are wrong, which is a bug that has already been fixed once here.\n */\n const hasAnnotations = file.annotationSignals.length > 0;\n const needsEveryRecordStart = file.header.continuity === 'EDF+D' && hasAnnotations;\n /*\n A continuous recording needs its origin too, which is one read rather than one per\n record: 0.4.9 made its first record's timekeeping TAL the point the samples are timed\n from, and this report went on placing a requested window against zero. `--info --start 1`\n predicted 8 rows where the conversion wrote 10, on a file whose discontinuous twin —\n identical but for the reserved field — agreed with itself.\n */\n /*\n And a file with an annotation channel and no marker at all, which is the one case this\n condition used to exclude and the one that most needs it.\n\n Without `EDF+C` or `EDF+D` the origin is not applied — the samples are timed from zero —\n but the annotation channel is found by label, so its events keep the onsets the file\n gives them and the two CSVs come out on clocks the origin apart. That is what\n MISSING_EDF_PLUS_MARKER says, and it is raised from the record starts, which this\n declined to read for exactly the files that produce it. So a conversion warned that\n signals.csv and annotations.csv would be a hundred seconds apart, and `--info` — run\n first, on purpose, to find out what a conversion would say — said nothing at all.\n\n The same single read the continuous case already pays for, on a file that is anomalous\n to begin with: an EDF Annotations channel is not something a plain EDF file has.\n */\n const scan =\n !needsEveryRecordStart && file.header.continuity !== 'EDF+D' && hasAnnotations\n ? await file.scanOrigin()\n : null;\n const annotationData = needsEveryRecordStart\n ? await file.readAnnotations()\n : {\n annotations: [],\n recordStarts: scan ? [scan.origin] : [],\n malformed: scan?.malformed ?? 0,\n // Counted rather than assumed: see EdfFile.scanOrigin.\n malformedTimekeeping: scan?.malformedTimekeeping ?? 0,\n // And how many of those took events with them, which decides whether the warning\n // beside it says nothing was lost. See EdfFile.scanOrigin.\n malformedTimekeepingWithText: scan?.malformedTimekeepingWithText ?? 0,\n };\n const timing = deriveRecordStarts(file, annotationData);\n const plan = buildPlan(\n {\n signals: file.header.signals,\n recordDuration: file.header.recordDuration,\n recordCount: file.recordCount,\n hasAnnotationChannel: file.annotationSignals.length > 0,\n recordStarts: timing.starts,\n },\n shared,\n );\n /*\n The event count, where reading the channel has already produced it.\n\n Only a discontinuous file, where every record start has to be read out of the annotation\n channel; a continuous one is read as far as the first record that states a start time and\n no further, so its events are not all in hand. Counted through the same window predicate\n a conversion filters them by, rather than a second copy of it.\n */\n const eventWindow = requestedAnnotationWindow(shared, plan.range.recordingStartSeconds);\n const knownEvents = needsEveryRecordStart\n ? annotationData.annotations.filter(\n (a) => a.onset >= eventWindow.from && a.onset < eventWindow.to,\n ).length\n : null;\n plan.diagnostics.push(...timing.diagnostics);\n /*\n What --stdout would do with this recording, which --info did not ask.\n\n `--info --stdout` on a three-rate file predicted \"Would write 1,155 rows, roughly\n 22.2 KB\" and said the channels \"are written to one file per rate\" — for a command that\n refuses to run, writes nothing and names no file. --info exists to say what a\n conversion will do, and refusing is one of the things it does.\n\n A warning rather than a refusal, for the reason 0.5.51 gives about the destination\n guards: --info writes nothing, so a rule about the output has no business stopping it\n from describing the recording — and being told the command will not work is exactly\n what was asked. The conversion's own guard supplies the words, so there is one wording.\n */\n /*\n What the conversion would say about the durations it would write.\n\n Same window the conversion applies, so --info predicts the warnings rather than a\n different set. Raised here rather than in the parser because the counts have to be of\n the rows that reach annotations.csv — see durationDiagnostics.\n\n `eventWindow` above and not the resolved range, which is what this passed. The two are\n the same window only while the conversion asks for no more than the recording holds:\n the range is clamped to the recording, and an annotation onset is not obliged to fall\n inside it — a marker for the end of a recording sits at exactly its length, and\n annotations-at-edges.edf exists because filtering events by the range dropped those.\n So a two-second EDF+D carrying one unreadable duration inside its span and one past the\n end was described by\n\n --info 1 annotation states a duration that is not a number, so its ...\n conversion 2 annotations state a duration that is not a number, so their ...\n\n about the same file with no time option given, over an annotations.csv holding both\n rows. `knownEvents` three lines up already counts through `eventWindow`, for the reason\n its comment gives; this was the second copy that comment says not to make.\n */\n plan.diagnostics.push(...durationDiagnostics(annotationData.annotations, eventWindow));\n\n /*\n And the warning about the signal file this conversion would not write.\n\n `--info` prints one accurate line about it in the report body — \"Would write\n channels.csv and no signal data\" — and raised nothing, so the one mode whose purpose is\n to say what a conversion will do carried a shorter warning list than the conversion did.\n `--info --strict` is documented as a cheap way to screen a directory before converting\n it, and on a recording of nothing but annotations it reported one warning where the\n conversion reports two.\n */\n const missing = noSignalFile(file, plan);\n if (missing) plan.diagnostics.push(missing);\n // The same for --annotations-only on a file with none; see noAnnotations.\n const noEvents = noAnnotations(file, shared as ConvertOptions);\n if (noEvents) plan.diagnostics.push(noEvents);\n\n if (toStdout) {\n const refusal = stdoutRefusal(file, plan);\n if (refusal) {\n plan.diagnostics.push({\n code: 'STDOUT_UNSUPPORTED',\n severity: 'warning',\n message: `--stdout would refuse this recording: ${refusal.message.replace(/^--stdout /u, '')}`,\n ...(refusal.hint === undefined ? {} : { hint: refusal.hint }),\n });\n }\n }\n /*\n Checked, like a conversion's stdout is.\n\n This wrote and looked at nothing, so `--info > desc.txt` into a filesystem with no room\n produced a zero-byte file and exited 0. The same audit the `--stdout` path uses: it\n declines anything that is not a regular file, so a pipe or a terminal is unaffected, and\n `--info | head` keeps exiting 0.\n */\n const audit = auditStdout();\n const description = asJson\n ? `${infoJson(file, plan, jsonIndent)}\\n`\n : `${formatInfo(file, plan, knownEvents)}\\n`;\n process.stdout.write(description);\n audit?.count(Buffer.byteLength(description));\n audit?.verify();\n\n // Under --json the warnings travel inside the document, exactly as they do for a\n // conversion, so stderr stays empty and the whole result is one parseable thing.\n const diagnostics = [...withTimingPromiseKept(withoutFileRateWarning(file.diagnostics), timing.starts !== null), ...plan.diagnostics];\n if (!asJson && diagnostics.length > 0) {\n /*\n Named when there is more than one recording to confuse it with.\n\n The table goes to stdout and the warnings to stderr, which is the point of the split\n — but over a folder that left several warnings in a row on stderr with nothing saying\n which recording raised any of them. Two recordings, two warnings, and no way to pair\n them up short of running the tool again one file at a time. A batch conversion has\n named its recordings since 0.4.20; this is the same report from the same tool about\n the same files.\n */\n const where = batch ? `${printable(input)}: ` : '';\n process.stderr.write(\n `\\n${formatDiagnostics(diagnostics).replace(/^(warning|note): /gmu, (m) => `${m}${where}`)}\\n`,\n );\n }\n return diagnostics.length;\n } finally {\n await file.close();\n }\n}\n\n/** Convert one recording, and return how many diagnostics it raised. */\nasync function convertOne(\n input: string,\n destination: string,\n options: Record<string, unknown> & {\n quiet: boolean;\n asJson: boolean;\n showProgress: boolean;\n toStdout: boolean;\n jsonIndent: number | null;\n batch?: boolean;\n },\n emit: Emit = writeThrough,\n): Promise<number> {\n const { quiet, asJson, showProgress, toStdout } = options;\n let lastTick = 0;\n\n /*\n Interrupting a conversion leaves a CSV that stops mid-recording but is still\n perfectly well-formed, so nothing about the file itself reveals that half the\n data is missing. Saying so on the way out is the whole point of a tool that\n claims it will not go quiet when something is wrong.\n */\n /*\n Whether the destination was already there before this run touched it.\n\n Read once, up front, because at interrupt time it is the difference between three\n different true statements and there is no other way to tell them apart. A directory that\n exists now and did not exist then was created by this conversion; one that existed then\n holds files this conversion may never have reached.\n */\n const destinationExistedBefore = toStdout ? false : existsSync(destination);\n\n /*\n Take the progress meter off the line before anything else is printed on it.\n\n The meter writes `\\r converting\\u2026 47%` and leaves the cursor after the percentage, so\n whatever is written next continues that line. Success cleared it and an interrupt cleared\n it; a conversion that *failed* did not, and the error landed on the end of the meter:\n\n converting\\u2026 96%error: Expected 317440 bytes of data at record 1638 but only 0 ...\n\n Which is the one thing this output is shaped to avoid. Every release since 0.7.1 has kept\n `error: ` and `warning: ` at the start of a line so a batch's stderr can be grepped for\n them, and here the prefix sat mid-line, where `grep \\'^error:\\'` over a failed run finds\n nothing at all. Only reachable on a terminal, which is why nothing caught it: the meter is\n off whenever stderr is not a TTY, and a captured stderr never is.\n */\n const clearProgress = (): void => {\n if (showProgress) process.stderr.write('\\r\\u001b[K');\n };\n\n const onInterrupt = (signal: NodeJS.Signals): void => {\n clearProgress();\n /*\n Say which of the three happened, rather than the middle one every time.\n\n --stdout writes no directory, so there is none to warn about. Naming one anyway\n pointed at a path that was never created — the same \"files that were never written\"\n that 0.2.30 removed from this path's error message. The directory case had the same\n defect one step further in: `convert()` hashes the input under --checksum and scans\n the whole annotation channel for record start times *before* it claims the directory,\n so a Ctrl-C in the first second of a large EDF+ printed \"Files already written to\n \"oa\" are incomplete and should not be used\" about a directory that `ls` then reported\n did not exist. Nothing had been written, and the advice was to distrust nothing.\n */\n const wrote = !toStdout && existsSync(destination);\n process.stderr.write(\n `\\ninterrupted (${signal}): the conversion stopped part way through.\\n` +\n (toStdout\n ? detail('The CSV on stdout stops mid-recording and should not be used.')\n : !wrote\n ? detail(`Nothing was written: \"${printable(destination)}\" was never created.`)\n : destinationExistedBefore\n ? detail(\n `\"${printable(destination)}\" was already there, so what is in it may be ` +\n `this run's incomplete output.`,\n )\n : detail(\n `Files already written to \"${printable(destination)}\" are incomplete and ` +\n `should not be used.`,\n )),\n );\n // 128 + signal number, the conventional exit status for dying to a signal.\n process.exit(signal === 'SIGINT' ? 130 : 143);\n };\n process.once('SIGINT', onInterrupt);\n process.once('SIGTERM', onInterrupt);\n\n try {\n const result = await convert(input, {\n ...options,\n onProgress: showProgress\n ? (progress): void => {\n const now = Date.now();\n if (now - lastTick < 100) return;\n lastTick = now;\n const percent =\n progress.recordsTotal === 0\n ? 100\n : Math.floor((progress.recordsDone / progress.recordsTotal) * 100);\n process.stderr.write(`\\r converting… ${percent}%`);\n }\n : undefined,\n });\n\n clearProgress();\n\n if (result.diagnostics.length > 0 && !asJson) {\n /*\n Named when nothing else is naming it.\n\n A batch prints a `[n/m] <path>` header before each recording, and that header is what\n pairs a warning with the file it came from. `--quiet` suppresses it — it is the\n summary line it is documented to suppress, and it took the attribution with it. Two\n recordings, two warnings, and no way to tell which raised which, while `error:` lines\n in the same run stay named because 0.4.20 prefixes those separately.\n\n Same fix 0.5.34 made for `--info` over a folder, which has no header to lose.\n */\n const where = options.batch === true && quiet ? `${printable(input)}: ` : '';\n /*\n The trailing blank line separates these warnings from the summary underneath them,\n so it belongs to the summary and goes when the summary does.\n\n `--quiet` printed it anyway: one stray blank line per recording, in the mode whose\n entire purpose is to print less. On a batch of five hundred that is five hundred of\n them in the log, and on a single recording it left `--quiet --strict` showing two\n blank lines between the warning and the \"--strict:\" line where the ordinary run\n shows one.\n */\n emit(\n 'err',\n `${formatDiagnostics(result.diagnostics).replace(/^(warning|note): /gmu, (m) => `${m}${where}`)}\\n${quiet ? '' : '\\n'}`,\n );\n }\n if (asJson) {\n // One object per line, so a batch is JSON Lines — `jq` reads a record at a time\n // rather than waiting for the whole run to finish.\n emit('out', `${summaryJson(result, options.jsonIndent as number | null)}\\n`);\n } else if (!quiet) {\n // With --stdout there is no directory to summarise, and the row count is the only\n // thing worth saying — on stderr, so the CSV on stdout stays clean.\n if (toStdout) {\n const rows = result.files[0]?.rows ?? 0;\n /*\n A reader that closed the pipe did not receive a conversion, so it does not get a\n conversion's summary. `edf2csv rec.edf --stdout | head -1` announced \"Wrote 52,507\n rows to stdout\" — a number that is neither the recording's 102,400 nor the one row\n head took, but however many had been formatted before the close was noticed.\n\n Whether the *conversion* stopped early is a separate question from whether the\n reader did, and 0.5.12 answered the first with the second. A 10,000-row recording\n whose CSV outruns the pipe buffer but fits one flush is written in full and only\n then meets the closed pipe: every row formatted, every row handed over, and the\n summary said \"The recording was not converted in full.\" The estimate's row count is\n exact, so the two cases can be told apart and told apart honestly — what reached\n the reader is not knowable from this side either way.\n */\n const expected = result.plan.estimate.rows;\n // Singular at one, like every other count. A window narrow enough to select a single\n // sample is an ordinary thing to pipe, and this said \"Wrote 1 rows to stdout.\" — the\n // third message in this family, after 0.5.74 and 0.5.78, each found because the sweep\n // that checks for it was narrower than the set of modes that print a count.\n const written = `${rows.toLocaleString('en-US')} ${rows === 1 ? 'row' : 'rows'}`;\n emit(\n 'err',\n !result.readerHungUp\n ? `Wrote ${written} to stdout.\\n`\n : rows < expected\n ? `Stopped: the reader closed the pipe after ${rows.toLocaleString('en-US')} of ` +\n `${expected.toLocaleString('en-US')} ${expected === 1 ? 'row' : 'rows'} had been ` +\n `written. The recording was ` +\n `not converted in full.\\n`\n : `Wrote ${written} to stdout, but the reader closed ` +\n `the pipe before the end, so not all of them reached it.\\n`,\n );\n } else {\n emit('err', `${formatSummary(result)}\\n`);\n }\n }\n return result.diagnostics.length;\n } catch (error) {\n // The meter is still on the line when a conversion throws, and the caller's `error: `\n // is the next thing written.\n clearProgress();\n throw error;\n } finally {\n process.off('SIGINT', onInterrupt);\n process.off('SIGTERM', onInterrupt);\n }\n}\n\n/** A recording to convert, and the path its output should be named after. */\ninterface Input {\n /** Where the recording is. */\n path: string;\n /**\n * What to call its output, relative to `--out`.\n *\n * For a file named on the command line this is just its own name. For one found by\n * expanding a directory it keeps the position it had inside that directory, so a study\n * laid out as one folder per night comes out the same shape — and, more to the point, so\n * that fifty recordings all named `rec.edf` do not all claim `<out>/rec`.\n */\n name: string;\n /**\n * Whether this recording was found by expanding a directory rather than named directly.\n *\n * `--out` means two different things — the output directory itself, or a parent to put one\n * directory per recording inside — and this is what decides which. See `destinationsFor`.\n */\n fromDirectory: boolean;\n}\n\n/**\n * Recordings to convert, with directories expanded to what is inside them.\n *\n * A directory yields every `.edf` and `.bdf` beneath it, at any depth. Recordings arrive\n * organised into folders, and a shell has no tidy way to reach them — which is why the\n * recipes here carried a `find` incantation to do it. Passing the folder is the obvious\n * thing to try, and it used to fail with \"is a directory, not an EDF file\".\n */\nasync function expandInputs(\n positionals: readonly string[],\n): Promise<{ inputs: Input[]; unreadable: string[]; namedDirectory: boolean }> {\n const found: Input[] = [];\n const unreadable: string[] = [];\n /*\n Whether a directory was NAMED, which is not the same as whether one yielded anything.\n\n `fromDirectory` on the inputs answers the second question, and using it for the first put\n the 0.4.20 defect back one step along: `edf2csv study named.edf --out csv` wrote\n csv/named/ while the study held recordings and csv/signals.csv once it held none, because\n the surviving input then looked like a lone file. Whether some unrelated folder happens\n to contain anything decided where a different recording's output went.\n */\n let namedDirectory = false;\n for (const given of positionals) {\n const info = await stat(given).catch(() => null);\n if (info === null || !info.isDirectory()) {\n // Anything that is not a directory is passed through untouched, so a file that does\n // not exist still reports itself rather than vanishing from the list.\n found.push({ path: given, name: path.basename(given), fromDirectory: false });\n continue;\n }\n namedDirectory = true;\n const walked = await walk(given);\n unreadable.push(...walked.unreadable);\n for (const file of walked.files) {\n found.push({ path: file, name: path.relative(given, file), fromDirectory: true });\n }\n }\n /*\n One recording, however many ways it was named.\n\n The walk already does this for links inside a folder, and the two halves disagreed:\n `edf2csv study` converted a recording reached twice once, while `edf2csv a.edf a.edf`\n refused the whole run for the collision it would cause. A shell makes the second easy\n to produce by accident — `edf2csv *.edf recording.edf` — and there is nothing ambiguous\n about it: it is one recording, and converting it once is what was meant.\n\n A path that does not resolve keeps its own identity so that a file which is not there\n still reports itself rather than being folded into another entry.\n\n Which of the names survives is decided by the names themselves, not by the order they\n turned up in. Keeping the first arrival meant the output directory was named by argument\n order — `edf2csv data/one.edf data/alias.edf` wrote out/one and the same two swapped\n wrote out/alias — and a shell orders a glob however it likes. Inside a folder it was the\n order `readdir` returned, which differs between filesystems, so copying a study to\n another machine could rename its output. A recording that is not a link is preferred over\n a link to it, since that is the name the recording actually has; two of a kind are\n settled by the path that sorts first.\n */\n const identified: { entry: Input; identity: string; link: boolean }[] = [];\n for (const entry of found) {\n const identity = await realpath(entry.path).catch(() => `?${path.resolve(entry.path)}`);\n const own = await lstat(entry.path).catch(() => null);\n identified.push({ entry, identity, link: own?.isSymbolicLink() ?? false });\n }\n\n const byIdentity = new Map<string, Input>();\n const winners = new Map<string, (typeof identified)[number]>();\n for (const candidate of identified) {\n const held = winners.get(candidate.identity);\n if (held !== undefined && !outnames(candidate, held)) continue;\n winners.set(candidate.identity, candidate);\n byIdentity.set(candidate.identity, candidate.entry);\n }\n\n // A directory hands its entries back in whatever order the filesystem stored them.\n const unique = [...byIdentity.values()];\n unique.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n\n /*\n One unreadable path, however many ways it was named — the same rule the recordings above\n get, which this did not.\n\n Each named directory is walked separately and its findings appended, so a folder given\n twice, given relatively and absolutely, or given alongside a link to it, reported every\n path it could not read once per name. `edf2csv study study` on a study holding one locked\n sub-directory printed the error twice and closed with \"Converted 1 of 1 recordings; 2\n paths could not be read\" — one path, counted two — while the recordings beside it were\n correctly converted once. The count is the part that matters: it is what tells someone how\n much of their study was not looked at.\n\n Identity, not spelling, for the reason 0.5.64 gives: `study/locked` and `alias/locked` are\n one directory. `realpathSync` resolves it even when the directory itself cannot be opened,\n since that needs search permission on the parent rather than on the target. A path that\n cannot be resolved at all keeps its own identity, so a name that is simply not there still\n reports itself.\n */\n const seen = new Set<string>();\n const distinct: string[] = [];\n for (const entry of unreadable) {\n let identity: string;\n try {\n identity = realpathSync(entry);\n } catch {\n identity = `?${path.resolve(entry)}`;\n }\n if (seen.has(identity)) continue;\n seen.add(identity);\n // The first spelling is kept, which is the one the caller typed first.\n distinct.push(entry);\n }\n\n return { inputs: unique, unreadable: distinct, namedDirectory };\n}\n\n/**\n * Which of two names for one recording the output should be called after.\n *\n * A name the recording actually has beats a link pointing at it, and two of a kind are\n * settled by sort order. Both are properties of the names, so the answer does not move when\n * a shell expands a glob differently or a filesystem enumerates a folder in another order.\n */\nfunction outnames(\n candidate: { entry: Input; link: boolean },\n held: { entry: Input; link: boolean },\n): boolean {\n if (candidate.link !== held.link) return !candidate.link;\n /*\n Resolved, not as typed. `study/night-01/rec.edf` and `./study/night-01/rec.edf` are the\n same file spelled two ways, and comparing the raw strings made them two different names —\n so this returned here, on lexicographic order of the spelling, and never reached the depth\n rule below that exists for exactly this case.\n\n What that cost is not cosmetic. `edf2csv study study/night-01/rec.edf` converted both\n recordings and exited 0; the identical request written `edf2csv study\n ./study/night-01/rec.edf` was refused with \"would both be converted into \"out/rec\", so one\n would overwrite the other\", exit 2, nothing written. Same files, same folder, same\n intention — and a leading `./` decided whether the run happened. Without the sibling that\n collides it was quieter and no better: one spelling wrote `out/night-01/rec` and the other\n `out/rec`, so a script that started passing absolute paths moved its output.\n\n Through `realpathSync`, not `path.resolve`, because a lexical resolve is not enough: on\n macOS `$TMPDIR` sits under `/var`, which is a link to `/private/var`, so a folder walked\n from the name the caller gave and a recording named relative to the process's own\n directory come out with different absolute prefixes for the same file. Falls back to the\n lexical form for a path that cannot be resolved, which is the same answer as before.\n\n Following links here is safe: a link and its target reach this as two genuinely different\n names, and the rule above has already preferred the real one, so the only pairs left with\n one identity are two spellings or two links — and both should fall through to the depth\n rule below rather than be settled by how they were typed.\n */\n const spelled = (entry: Input): string => {\n try {\n return realpathSync(entry.path);\n } catch {\n return path.resolve(entry.path);\n }\n };\n if (spelled(candidate.entry) !== spelled(held.entry)) {\n return spelled(candidate.entry) < spelled(held.entry);\n }\n /*\n One recording, one path, two names — reached directly and through a named folder.\n\n `edf2csv study study/night-01/rec.edf` and the same two swapped are the same request, and\n they disagreed: the folder gives the recording its position inside the folder\n (`night-01/rec`), a direct mention gives it its bare name (`rec`), and whichever spelling\n the loop met first won. So argument order decided the output directory's name, and, once\n a second `study/rec.edf` was in play, decided whether the run happened at all — the bare\n name collides with it and the run is refused, exit 2, while the other order converts both.\n\n The nested name wins. It is what the folder promised — \"the layout is kept: recordings in\n sub-folders come out in sub-folders\" — and it is the one that does not collide, since\n collapsing a recording to its bare name is what puts it on top of a sibling.\n */\n const depth = (entry: Input): number => entry.name.split(path.sep).length;\n if (depth(candidate.entry) !== depth(held.entry)) return depth(candidate.entry) > depth(held.entry);\n return candidate.entry.name < held.entry.name;\n}\n\n/**\n * Every recording under a directory, following symbolic links.\n *\n * Written by hand rather than with a recursive `readdir` because that reports a symlink as\n * a symlink and never as a file, so a linked recording was skipped without a word — and the\n * closing \"converted 3 of 3\" then described the three it had noticed rather than what was in\n * the folder. Data organised by linking recordings into a working directory is ordinary, and\n * quietly converting fewer files than were asked for is the failure this tool exists to\n * avoid. Naming the same link on the command line always worked, which made the omission\n * harder to notice rather than easier.\n *\n * Following links means they can form a cycle, so directories are recorded by their resolved\n * identity and visited once. A link to a file already reached another way is likewise\n * converted once.\n */\nasync function walk(root: string): Promise<{ files: string[]; unreadable: string[] }> {\n const files: string[] = [];\n const unreadable: string[] = [];\n const seen = new Set<string>();\n /*\n A queue, taken from the front, and each directory's children entered in a settled order.\n\n This was a stack popped from the back, so which of two names for one directory was\n visited first came down to the order `readdir` happened to return them — and the loser\n was then skipped as already seen, taking its name out of the run with it. A folder\n holding `aaa-real/` beside `zzz-alias -> aaa-real` converted into `<out>/zzz-alias/`: the\n link's name, chosen by a hash order that differs between filesystems. 0.4.29 settled this\n for two names of one *file* and left the directory above it deciding by accident.\n\n Breadth first, and each directory's sub-directories entered with real names before links\n and alphabetically within each, so the name that survives is a property of the tree: the\n shallowest, then the one that is not a link, then the first in sort order.\n */\n const queue = [root];\n let next = 0;\n\n while (next < queue.length) {\n const directory = queue[next++] as string;\n const real = await realpath(directory).catch(() => directory);\n if (seen.has(real)) continue;\n seen.add(real);\n\n /*\n A directory that cannot be listed is reported, not stepped over.\n\n Skipping it in silence meant a folder holding three recordings, one of them inside a\n sub-directory without read permission, converted two and said \"Converted 2 of 2\n recordings\" — a total that agreed with itself and with nothing else. That is the same\n failure 0.4.4 fixed for symbolic links, arriving by a different route: converting fewer\n recordings than were asked for and reporting success.\n */\n let entries;\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch {\n unreadable.push(directory);\n continue;\n }\n\n // A real name before a link to the same thing, then alphabetically. The dirent describes\n // the entry itself rather than its target, which is exactly the question here.\n const ordered = [...entries].sort((a, b) => {\n const link = Number(a.isSymbolicLink()) - Number(b.isSymbolicLink());\n return link !== 0 ? link : a.name < b.name ? -1 : a.name > b.name ? 1 : 0;\n });\n\n for (const entry of ordered) {\n const full = path.join(directory, entry.name);\n // stat, not the dirent: a dirent describes the link, and what matters is its target.\n const info = await stat(full).catch(() => null);\n if (info === null) {\n /*\n Anything that cannot be inspected is reported, whatever it is called.\n\n This used to report only names ending in `.edf` or `.bdf`, on the reasoning that a\n broken link to something else is nobody's business. A directory carries no such\n name. A study kept as one folder per night, with one night linked to an external\n drive, converted the nights that were mounted and said nothing about the one that\n was not — and because losing that input left a single recording, `--out` stopped\n meaning \"a parent to fill\" and started meaning \"the directory to write\", so the\n survivor landed somewhere else as well. Whether a drive happened to be mounted\n changed both what was converted and where it went, in silence, exit 0.\n\n The walk cannot know what was behind a link it cannot follow, which is exactly the\n reason to say so rather than to guess.\n */\n unreadable.push(full);\n continue;\n }\n if (info.isDirectory()) {\n queue.push(full);\n } else if (info.isFile() && /\\.(edf|bdf)$/iu.test(entry.name)) {\n // Duplicates are collapsed once for the whole list in expandInputs; this only has\n // to find them. The `seen` set here is for directory cycles.\n files.push(full);\n }\n }\n }\n return { files, unreadable };\n}\n\n/**\n * Where each recording's output goes.\n *\n * Naming one recording, `--out` is the output directory itself, which is what it has always\n * meant. Naming a folder, or several recordings, it is a parent and each recording gets its\n * own directory inside it, because writing several recordings into one directory would have\n * them overwrite each other's `signals.csv` — the one thing a batch must not do quietly.\n *\n * With no `--out` at all, every recording converts beside itself exactly as it would have\n * done alone, so a glob behaves like the shell loop it replaces.\n *\n * What a folder means is decided by the folder, not by how much is in it. Counting the\n * recordings instead meant `edf2csv study --out csv` wrote `csv/signals.csv` while the study\n * held one night and `csv/night-01/rec/signals.csv` once it held two: adding a recording\n * moved the output of a recording that had not changed. The same count made the destination\n * depend on things no one had touched either — a night on an unmounted drive, a\n * sub-directory that could not be read — so where the data landed turned on the state of the\n * machine rather than on the command.\n */\nfunction destinationsFor(\n inputs: readonly Input[],\n out: string | undefined,\n /** True when a folder was named, or more than one recording was. See `batch` in `main`. */\n batch: boolean,\n): string[] {\n if (out === undefined) return inputs.map((input) => defaultOutputDir(input.path));\n if (!batch) return [out];\n return inputs.map((input) => path.join(out, stemOf(input.name)));\n}\n\n/** A name without its extension, keeping a leading dot: `.hidden.edf` -> `.hidden`. */\nfunction stemOf(name: string): string {\n const dot = name.lastIndexOf('.');\n const slash = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\\\'));\n return dot > slash + 1 ? name.slice(0, dot) : name;\n}\n\n/**\n * Refuse a batch in which two recordings would land in the same directory.\n *\n * Two files with the same name in different folders — `night-1/rec.edf` and\n * `night-2/rec.edf`, which is how recordings usually get organised — both resolve to\n * `<out>/rec`. Converting them in turn would leave one recording's data sitting under the\n * other's name with nothing to show it had happened, so the run stops before writing\n * anything at all.\n */\nfunction assertDistinct(inputs: readonly string[], destinations: readonly string[]): void {\n /*\n Two names that differ only in case are one directory on a filesystem that does not\n distinguish them, which is the default on macOS and the rule on Windows.\n\n `a/REC.edf` and `b/rec.edf` produce `<out>/REC` and `<out>/rec`. Compared exactly those\n are different, so both went through, and with --force the second conversion wrote into\n the directory the first had made: one directory holding one recording's `signals.csv`\n beside the other's `signals_256hz.csv`, under a single `metadata.json` naming only one\n of them. The run reported \"Converted 2 of 2 recordings\" and exited 0. A directory whose\n provenance file describes a recording other than the data beside it is the one outcome\n this tool exists to prevent.\n\n The comparison follows the platform rather than being applied everywhere, so a\n case-sensitive filesystem — where those really are two directories — keeps converting\n both. A case-sensitive volume on macOS is the exception it gets wrong, and it gets it\n wrong in the safe direction: a refusal that names both recordings, not a silent merge.\n */\n const foldsCase = process.platform === 'darwin' || process.platform === 'win32';\n /*\n macOS filesystems fold Unicode normalisation as well as case.\n\n HFS+ and APFS compare names in a normalised form, so `café` written as e + U+0301 and\n `café` written as U+00E9 are one directory — while remaining two different JavaScript\n strings, which is all this guard was comparing. Two recordings whose stems differ only\n that way therefore both passed the check and both converted into the same place:\n\n study/café.edf (NFC) -> csv/café/signals.csv\n study/café.bdf (NFD) -> csv/café/signals_256hz.csv, _128hz, _1hz\n\n one directory holding both, under a single metadata.json naming one of them, reported as\n \"Converted 2 of 2 recordings\" and exit 0 under --force. Without --force the second run\n happened to hit \"already exists\", which is the accidental save rather than the check\n doing its job — and it named the wrong problem.\n\n Not folded on Linux, where the two names are genuinely two directories and refusing them\n would be inventing a collision that does not exist. Windows preserves normalisation too,\n so only darwin. This is the same platform-shaped assumption the case fold above already\n makes, and it has the same limit: a network or removable volume that normalises while the\n running platform does not is not covered by either.\n */\n const identity = (destination: string): string => {\n const resolved = path.resolve(destination);\n if (!foldsCase) return resolved;\n const folded = resolved.toLowerCase();\n return process.platform === 'darwin' ? folded.normalize('NFC') : folded;\n };\n\n const claimed = new Map<string, string>();\n for (const [index, destination] of destinations.entries()) {\n const key = identity(destination);\n const first = claimed.get(key);\n if (first !== undefined) {\n throw new OptionError(\n `\"${printable(inputs[index] as string)}\" and \"${printable(first)}\" would both be converted into \"${printable(destination)}\", ` +\n `so one would overwrite the other.\\n` +\n `Convert them separately, or rename one of them.`,\n );\n }\n claimed.set(key, inputs[index] as string);\n }\n\n /*\n And refuse one destination sitting inside another.\n\n A recording named `rec.edf` beside a folder named `rec` is enough: their outputs are\n `<out>/rec` and `<out>/rec/inner`, which are not equal, so the check above let both\n through. What happened next depended on which conversion got there first. Each creates\n its own directory with a single non-recursive mkdir but its parents recursively, so\n whichever started second either claimed a directory the other had already made as a\n parent — failing with \"already exists\" — or did not. Five runs in twenty failed that way\n under --jobs 2, converting one recording of two; the same command succeeded the other\n fifteen times.\n\n Sorting first puts an ancestor next to its descendant: anything sorting between them\n shares the same prefix, and would be caught as its own adjacent pair.\n */\n /*\n Each destination is checked against its own ancestors, by name.\n\n The first version of this sorted the resolved paths and compared neighbours, on the\n reasoning that an ancestor and its descendant end up adjacent. They do not: the\n separator is not the lowest character, so any sibling whose name begins with one of the\n thirteen printable characters below '/' lands between them. With `rec.edf`, `rec!x.edf`\n and `rec/inner.edf` in one folder, '!' sorts between `out/rec` and `out/rec/inner` and\n the pair was never compared — three recordings converted, one of them inside another's\n directory, reported as \"Converted 3 of 3\".\n\n Walking up from each destination has no such gap. Output trees are shallow, so this is a\n handful of string lookups per recording.\n */\n const byDestination = new Map<string, number>();\n for (const [index, destination] of destinations.entries()) {\n byDestination.set(identity(destination), index);\n }\n\n for (const [index, destination] of destinations.entries()) {\n let ancestor = path.dirname(path.resolve(destination));\n for (let parent = ''; ancestor !== parent; ancestor = path.dirname(ancestor)) {\n parent = ancestor;\n const owner = byDestination.get(identity(ancestor));\n if (owner === undefined || owner === index) continue;\n /*\n All four paths escaped, not the two nearest the front of the sentence.\n\n A file name may hold a newline on every platform this runs on, and `printableLines`\n splits a message on newlines to indent its continuations — so the two raw paths broke\n the sentence into three lines and printed half a path on each:\n\n error: \"study/re\\x0ac/inner.edf\" would be converted into \"o/re\\x0ac/inner\",\n which is inside \"o/re\n c\" — where \"study/re\n c.edf\" is converted.\n\n Two of the four names read as one thing and two as two, in the same sentence, about\n the same pair of paths. That is 0.5.67's defect — a name holding a newline splitting\n `Wrote` across two lines — in the one message that names four paths at once.\n */\n throw new OptionError(\n `\"${printable(inputs[index] as string)}\" would be converted into \"${printable(destination)}\", which is inside ` +\n `\"${printable(destinations[owner] as string)}\" — where \"${printable(inputs[owner] as string)}\" is converted.\\n` +\n `One recording's output cannot sit inside another's. Convert them separately, or ` +\n `rename one of them.`,\n );\n }\n }\n}\n\n/**\n * The exit code for a run in which several files failed for different reasons.\n *\n * 2 means \"you invoked it wrong\" and 1 means \"something went wrong with a file\". When both\n * happened, 1 is the honest answer: the invocation cannot be the whole story once a file has\n * genuinely failed. With a single input there is only ever one code, so its exit status is\n * exactly what it always was.\n */\nfunction worstOf(codes: readonly number[]): number {\n /*\n Anything that is not \"the command line is the problem\" is a failure of the run.\n\n This tested for `EXIT_ERROR` and fell through to `EXIT_USAGE` for everything else, on the\n assumption that a child exits 1 or 2. A child killed by a signal exits 130 or 143 — its\n own interrupt handler does that — so a `--jobs` worker stopped by SIGTERM made the batch\n exit 2, which cli-reference's table defines as \"The command line is the problem\" and\n warnings-and-errors as \"The command was invoked incorrectly, or asked for something the\n recording can't provide\". The command was fine; something killed a worker. The\n out-of-memory killer and a scheduler's time limit both arrive exactly this way, and the\n serial path reports the same event as the failure it is.\n\n Asking the question the other way round is also the honest one: 2 is the narrow claim,\n so it should be the one that has to be earned by every code in the list.\n */\n return codes.some((code) => code !== EXIT_USAGE) ? EXIT_ERROR : EXIT_USAGE;\n}\n\n/**\n * Convert one recording in a separate process, and collect everything it printed.\n *\n * The arguments are rebuilt from the parsed options rather than sliced out of argv, so the\n * child receives exactly the flags this run was given and nothing that only makes sense to\n * the parent: not the other recordings, not --jobs, and not --info or --stdout, neither of\n * which reaches this path.\n */\nasync function convertInChild(\n input: string,\n destination: string,\n values: Record<string, unknown>,\n running: Map<ChildProcess, string>,\n): Promise<{\n code: number;\n out: string;\n err: string;\n report: { converted: number; warnings: number } | null;\n}> {\n /*\n The recording goes last, behind `--`.\n\n As the first argument it was parsed as an option whenever its path began with a dash,\n which `path.join` produces from a folder given as `.` — `./-lead.edf` normalises to\n `-lead.edf`. The child then failed on a file the parent had converted happily, so the\n same command converted two recordings serially and one under --jobs.\n */\n /*\n Option values go in the `--flag=value` form, never as two arguments.\n\n Split across two arguments, a value beginning with a dash is another option as far as the\n child's parser is concerned: `--out ./-nightly` reached it as `--out` followed by\n `-nightly`, and the child died on \"Option '--out' argument is ambiguous\" while the serial\n path converted the same command without complaint. A leading dash is not exotic —\n `path.join` produces one from a folder given as `.`, and directories get named after\n dates and flags often enough. Same failure 0.4.19 fixed for the recording's own path, on\n everything that carries a value rather than on the positional.\n */\n const args = [`--out=${destination}`];\n /*\n --strict is deliberately absent: it is a verdict on the whole run, and a child converting\n one recording is not the whole run. Passing it down made each child announce \"--strict: 1\n warning raised, so this run is reported as a failure\" about its own single file, and exit\n 1 for it, which the parent then counted as a conversion that had not happened. The parent\n applies it once, from the counts the children report.\n */\n for (const flag of ['annotations-only', 'checksum', 'gzip', 'bom', 'force', 'quiet', 'json']) {\n if (values[flag] === true) args.push(`--${flag}`);\n }\n for (const flag of ['start', 'duration', 'end', 'decimals', 'layout']) {\n if (typeof values[flag] === 'string') args.push(`--${flag}=${values[flag] as string}`);\n }\n // --channels is repeatable, and each term is passed as given so that a label containing a\n // comma survives: joining them back into one list would split it in the child.\n const channels = values['channels'];\n if (channels !== undefined) {\n for (const term of Array.isArray(channels) ? (channels as string[]) : [String(channels)]) {\n args.push(`--channels=${term}`);\n }\n }\n args.push('--', input);\n\n return new Promise((resolve) => {\n const child = fork(fileURLToPath(import.meta.url), args, {\n stdio: ['ignore', 'pipe', 'pipe', 'ipc'],\n });\n running.set(child, destination);\n let out = '';\n let err = '';\n let report: { converted: number; warnings: number } | null = null;\n child.on('message', (message: unknown) => {\n const payload = (message as { edf2csv?: { converted: number; warnings: number } })?.edf2csv;\n if (payload) report = payload;\n });\n child.stdout?.setEncoding('utf8').on('data', (chunk: string) => {\n out += chunk;\n });\n child.stderr?.setEncoding('utf8').on('data', (chunk: string) => {\n err += chunk;\n });\n child.on('error', (error) => {\n running.delete(child);\n /*\n Escaped, which every other place this file prints a recording's name has been since\n 0.5.67 and this one was not.\n\n A file name is untrusted text — the filesystem supplies it and a recording inside a\n scanned folder may be called anything, including a name carrying an ESC byte — and\n `named`, `reportError`, the `[n/m]` header and the interrupt handler all put it\n through `printable`. This line is the one that fires when the fork itself fails, which\n is the moment a batch of five hundred at `--jobs auto` runs out of file descriptors,\n and it is the only line in this file that prints a name straight to stderr.\n */\n resolve({\n code: EXIT_ERROR,\n out,\n err: `${err}error: ${printable(input)}: ${printableLines(error.message, ' ')}\\n`,\n report,\n });\n });\n child.on('close', (code, signal) => {\n running.delete(child);\n /*\n A child that was killed says nothing on its way out, so the parent has to.\n\n `code` is null when a process dies by signal, which left the parent with `code ??\n EXIT_ERROR` — a failure with an empty `err`, so the run printed nothing but \"Converted\n 1 of 2 recordings; 1 failed.\" and stopped. Nothing said which recording, nothing said\n why, and nothing said that its directory held a 194 MB signals.csv cut off mid-row\n with no channels.csv beside it. That file looks exactly like a finished one to\n anything that opens it.\n\n The out-of-memory killer, a job scheduler's time limit and `kill` all arrive this way.\n A run stopped from the keyboard is reported by the interrupt handler instead, which\n names every directory at once rather than one line per child.\n */\n if (signal !== null && !stopping) {\n // And the same question of this one child, for the same reason as the handler above.\n err +=\n `error: stopped by ${signal} before it finished.\\n` +\n detail(\n existsSync(destination)\n ? `Incomplete, and should not be used: ${printable(destination)}`\n : `Nothing was written: \"${printable(destination)}\" was never created.`,\n );\n }\n resolve({ code: code ?? EXIT_ERROR, out, err, report });\n });\n });\n}\n\n/**\n * Put the recording's name into the lines a child produced.\n *\n * Errors always: a failure has to say which recording failed, whatever else is on screen.\n * Warnings only when `alsoWarnings` — under --quiet, where the `[n/m]` header that would\n * otherwise carry the attribution is not printed. See the call site.\n */\nfunction named(text: string, input: string, alsoWarnings = false): string {\n // A function, not a string: `$&`, `$\\'`, `` $` `` and `$1` in a replacement string are\n // patterns, and a file may legitimately be called any of them. `bad$&name.edf` re-injected\n // the text it had just matched and reported itself as `baderror: name.edf`.\n const heads = alsoWarnings ? /^(error|warning|note): /gmu : /^error: /gmu;\n return text.replace(heads, (head) => `${head}${printable(input)}: `);\n}\n\n/** Re-render a child's pretty-printed summary onto one line, leaving anything else alone. */\nfunction compactJson(text: string): string {\n const trimmed = text.trim();\n if (trimmed === '') return '';\n try {\n return `${JSON.stringify(JSON.parse(trimmed))}\\n`;\n } catch {\n // Not the document expected; passing it through unchanged beats losing it.\n return text;\n }\n}\n\n/**\n * Where a conversion's output goes.\n *\n * Serial runs write straight through, which is what they have always done. Running several\n * conversions at once needs the alternative: each one's lines are collected and released in\n * a block when it finishes, so two recordings finishing together cannot interleave a summary\n * with a warning belonging to the other file.\n */\ntype Emit = (stream: 'out' | 'err', text: string) => void;\n\nconst writeThrough: Emit = (stream, text) => {\n (stream === 'out' ? process.stdout : process.stderr).write(text);\n};\n\n/** Collects output so it can be released in one piece when a conversion finishes. */\nfunction buffered(): { emit: Emit; flush: () => void } {\n const parts: [('out' | 'err'), string][] = [];\n return {\n emit: (stream, text) => parts.push([stream, text]),\n flush: () => {\n for (const [stream, text] of parts) writeThrough(stream, text);\n },\n };\n}\n\n/**\n * Report an error the way this command always has, and give the exit code it implies.\n *\n * `input` names the recording while a batch is running: several failures otherwise arrive\n * as a stack of messages with nothing saying which file each belongs to.\n */\nfunction reportError(error: unknown, input?: string, emit: Emit = writeThrough): number {\n const where = input === undefined ? '' : `${printable(input)}: `;\n if (error instanceof EdfError || error instanceof ConversionError) {\n emit('err', `error: ${where}${printableLines(error.message, ' ')}\\n`);\n /*\n Wrapped, for the same reason the hint under a warning is — and because on this file\n it is frequently the same sentence.\n\n A mixed-rate recording refused by --stdout and the same recording described by --info\n both end in \"Narrow it to one rate with --channels, write --layout long ...\". One\n arrived as a ConversionError hint and printed on a single 130-column line; the other\n arrived as a Diagnostic hint and wrapped at 80 from 0.7.1. Identical advice about an\n identical file, laid out two different ways depending on whether the tool went on to\n convert it.\n */\n /*\n Escaped, which the message above it has been since 0.5.67 and this line never was.\n\n A hint is usually a fixed sentence, which is why it looked like text nobody supplies.\n One is not: a read that fails part way through a conversion reports \"What was written\n to <outputDir> before it failed is incomplete\", and `--out` takes whatever path the\n caller gives it. A directory may be named with an ESC byte on every platform this\n runs on, so `--out $'out\\x1b[31mred'` on a recording that shrinks mid-read put a live\n colour change on stderr — under an `error:` line that had been escaped correctly, two\n lines above.\n */\n if (error.hint) emit('err', `${wrap(printable(error.hint), ' ')}\\n`);\n // A request the tool cannot carry out is the command line's problem, not the file's,\n // whatever layer noticed it. See USAGE_ERROR_CODES.\n return error instanceof ConversionError && USAGE_ERROR_CODES.has(error.code)\n ? EXIT_USAGE\n : EXIT_ERROR;\n }\n if (\n error instanceof ChannelSelectionError ||\n error instanceof TimeRangeError ||\n error instanceof OptionError\n ) {\n emit('err', `error: ${where}${printableLines(error.message, ' ')}\\n`);\n return EXIT_USAGE;\n }\n emit('err', `error: ${where}${message(error, ' ')}\\n`);\n return EXIT_ERROR;\n}\n\n/**\n * How many conversions to run at once.\n *\n * `auto` is one per core, less one, so a long batch does not take the machine over. A batch\n * is the only place this means anything: a single recording is a single conversion however\n * many jobs are asked for.\n */\nfunction parseJobs(raw: unknown, inputs: number): number {\n if (raw === undefined) return 1;\n const text = String(raw).trim();\n /*\n The cores this process may use, not the ones the machine has.\n\n `cpus().length` counts every core the kernel can see, which is the machine's answer to a\n question about this process. A container given two CPUs of a sixty-four core node, a job\n pinned by `taskset` or a scheduler, a `docker --cpuset-cpus` — in all of them `auto` asked\n for sixty-three workers, which is exactly the \"leaves the machine usable\" this setting\n exists for, inverted. `availableParallelism` is the call for the question and has been\n there since Node 18.14; this package requires 20.\n */\n if (text === 'auto') return Math.max(1, Math.min(inputs, availableParallelism() - 1));\n /*\n A whole number as written, not as `Number()` chooses to read it.\n\n `Number.isInteger(Number(text))` accepted `0x10`, `1e3` and `999999999999999999999` — the\n last of which is not an integer any more, only the nearest double to one — while refusing\n `4.7`. The message says \"a whole number of 1 or more\" and the refusal of 4.7 is what makes\n a reader believe it, so the others read as the tool silently reinterpreting what they\n typed. `--jobs` is the flag 0.4.2 hardened because accepting `--jobs 0` in silence \"is the\n kind of quiet this tool avoids\".\n\n Surrounding space is trimmed above and is not one of those: ` 4 ` is four to anyone who\n reads it, and a value assembled by a shell out of a file or a `$(...)` routinely carries\n some. What matters is that the refusal quote what was typed rather than what survived the\n trim — see below.\n */\n if (!/^\\d+$/u.test(text)) {\n throw new OptionError(`--jobs must be a whole number of 1 or more, or \"auto\", got \"${quotedValue(raw)}\".`);\n }\n const value = Number(text);\n if (!Number.isSafeInteger(value) || value < 1) {\n throw new OptionError(`--jobs must be a whole number of 1 or more, or \"auto\", got \"${quotedValue(raw)}\".`);\n }\n return Math.min(value, Math.max(1, inputs));\n}\n\nfunction splitChannels(raw: unknown): string[] | undefined {\n if (raw === undefined) return undefined;\n const list = Array.isArray(raw) ? (raw as string[]) : [String(raw)];\n const terms = list.flatMap((entry) => entry.split(',')).map((t) => t.trim()).filter((t) => t !== '');\n // Returning undefined here would mean \"no --channels given\" and convert everything,\n // which is the opposite of what someone passing an empty list is asking for.\n if (terms.length === 0) {\n throw new OptionError('--channels was given but lists no channel names.');\n }\n return terms;\n}\n\n/**\n * `--start`, `--duration` and `--end`, in seconds.\n *\n * The first two name a position on the recording's own clock, which a file timed from before\n * zero puts below it, so those take a sign. `--duration` is a length and does not.\n */\nfunction optionalTime(raw: unknown, option: string): number | undefined {\n if (raw === undefined) return undefined;\n return parseTimeSpec(String(raw), option, option !== '--duration');\n}\n\n/** `--layout`, which is one of two words and not a guess at what was meant. */\nfunction optionalLayout(raw: unknown): 'wide' | 'long' | undefined {\n if (raw === undefined) return undefined;\n // Trimmed, like every other option's value. This one alone refused ` long`, so a value that\n // came out of a file or a `$(...)` with a newline on it was rejected for a character nobody\n // typed and which the message could not show.\n const text = String(raw).trim();\n if (text === 'wide' || text === 'long') return text;\n throw new OptionError(`--layout must be \"wide\" or \"long\", got \"${quotedValue(raw)}\".`);\n}\n\n/**\n * An option's value as the user typed it, for a message that quotes it back.\n *\n * The point of the quotation marks is to show where the value begins and ends, which matters\n * most for the values that went wrong because of what surrounds them. `--jobs` trimmed first\n * and then quoted the remains, so `--jobs \" x\"` came back as `got \"x\"` and `--jobs \" \"` as\n * `got \"\"` — the second reading as though nothing had been given at all, when what was given\n * is the whole reason it failed. `--start` has quoted the typed value since 0.5.60 and\n * `--decimals` since 0.5.86; this is the same rule, spelled once.\n */\nfunction quotedValue(raw: unknown): string {\n return String(raw);\n}\n\nfunction optionalDecimals(raw: unknown): number | undefined {\n if (raw === undefined) return undefined;\n const text = String(raw).trim();\n // Number('') is 0, which would quietly round every physical value to a whole number.\n if (text === '') {\n throw new OptionError('--decimals needs a number, for example --decimals 3.');\n }\n /*\n A whole number as written, not as `Number()` chooses to read it.\n\n `Number.isInteger(Number(text))` accepted `0x3`, `0b11`, `0o5`, `3e0` and `+3`,\n while refusing `3.5` — and the refusal of `3.5` is what makes a reader believe the message\n that says \"a whole number between 0 and 20\". So `--decimals 0o5` wrote five decimals for\n an argument that reads as five to nobody, and `--decimals 0b11` wrote three, in silence,\n exit 0, with a CSV that looks exactly as intended.\n\n The same fix `--jobs` got, and the same one `--channels '#N'` got — where the comment\n records why: \"Every one of them selected a channel and exited 0, so a slip did not fail —\n it quietly converted a different channel than the one asked for, which for this tool is\n the worst way to be wrong.\" A precision is the same kind of quiet.\n */\n const value = Number(text);\n if (!/^\\d+$/u.test(text) || !Number.isInteger(value) || value < 0 || value > 20) {\n throw new OptionError(`--decimals must be a whole number between 0 and 20, got \"${String(raw)}\".`);\n }\n return value;\n}\n\n/*\n Error text is escaped for the same reason --info's table is: a fatal header error quotes\n the channel label that caused it, and that label is free text out of the file. A recording\n declaring a negative sample count under a label containing `\\x1b[2J` cleared the reader's\n screen on the way out.\n*/\n/**\n * A `parseArgs` failure, in this tool's words where they are better than Node's.\n *\n * Node refuses `--out -nightly` with \"Option '--out' argument is ambiguous. Did you forget to\n * specify the option argument for '--out'? To specify an option argument starting with a dash\n * use '--out=-XYZ'.\" The user did not forget anything — they gave a value — and `-XYZ` is a\n * placeholder, where every other message this tool prints quotes what was actually typed and\n * says exactly what to type instead. A destination whose name starts with a dash is not exotic,\n * and neither is a negative `--start` on a recording timed from before zero.\n *\n * 0.4.34 already fixed this message where the tool produced it internally, building child argv\n * for `--jobs` — `--out ./-nightly` reached the child as two arguments and died on it, while the\n * serial path converted the same command. The half a user can hit was left as Node wrote it.\n *\n * An unknown option is reworded too, and for a plainer reason than tone. Node's sentence is\n * \"Unknown option '--chanels'. To specify a positional argument starting with a '-', place it at\n * the end of the command after '--', as in '-- \"--chanels\"' — except the closing quote is not\n * there. Node opens one before `--` and never closes it, so this tool printed an unfinished\n * sentence, without the `error:` prefix every other refusal carries, at the single most common way\n * to get a command wrong. The advice is also for a different mistake than the one almost everyone\n * makes: it explains how to pass a *file* whose name begins with a dash, where the reader has\n * mistyped one of twenty flags.\n *\n * Node's remaining two — a switch given a value, an option missing its value — say something true\n * in words a reader can act on, and replacing them with near-identical sentences would be churn.\n * They do go out with the prefix, which is the other half of what was wrong with the unknown-option\n * message above and was left behind when its wording was fixed:\n *\n * $ edf2csv recording.edf --decimals\n * Option '--decimals <value>' argument missing\n * $ edf2csv recording.edf --gzip=yes\n * Option '--gzip' does not take an argument\n *\n * against `error:` on every other refusal this tool prints, including the one for `--decimals 101`\n * one keystroke away. This repository's own stdout audit picks errors out of stderr with\n * `line.startsWith('error:')`, so by its own reckoning these two runs printed no error and exited 2.\n */\n/**\n * A token as it has to be typed for a shell to hand it over unchanged.\n *\n * Every hint in this file that prints a command has to print one that works — the comment on\n * the `--channels` advice in the header parser lists three separate times it did not. These\n * two are the same failure at the command line rather than in a channel label. `--out \"-my\n * nightly\"` was answered with `Write it as one argument instead: --out=-my nightly`, which is\n * two arguments; `--chan\"nels` was answered with `edf2csv -- \"--chan\"nels\"`, whose quotes\n * collapse into something else again.\n *\n * Left bare when a shell would read it as written, so the ordinary `--out=-nightly` reads as\n * it always has. Otherwise single-quoted, the one POSIX form with no escapes inside it: a\n * single quote in the token closes, escapes and reopens.\n */\nfunction survivesBare(text: string): boolean {\n return text !== '' && !/[^\\w@%+=:,./-]/u.test(text);\n}\n\nfunction singleQuoted(text: string): string {\n return `'${text.replaceAll(\"'\", \"'\\\\''\")}'`;\n}\n\nfunction usageMessage(error: unknown, argv: readonly string[]): string {\n /** Node's own wording, carrying this tool's prefix. */\n const asError = (text: string): string => (text.startsWith('error:') ? text : `error: ${text}`);\n const raw = message(error);\n const code = (error as { code?: unknown } | null)?.code;\n if (code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') return unknownOption(raw);\n if (code !== 'ERR_PARSE_ARGS_INVALID_OPTION_VALUE') {\n return asError(raw);\n }\n const ambiguous = /^Option '(-[^']+)' argument is ambiguous/u.exec(raw);\n if (!ambiguous) return asError(raw);\n\n const flag = ambiguous[1] as string;\n /*\n The value is whatever followed the flag. Read from argv rather than from the message,\n which does not carry it — and if it is not there after all, Node's text is still true.\n\n The occurrence Node stopped at, which is the first one whose value begins with a dash and\n not simply the first occurrence of the flag. A flag given twice put the wrong value in the\n sentence: `--end 1e3 --end -5` was refused with `--end was given \"1e3\", which begins with\n a dash`, over a value that does not, advising `--end=1e3` — a rewrite of the half that was\n already fine, leaving the half that was not exactly as it was. `--channels` is repeatable,\n so two of them is an ordinary command rather than a mistake, and that is where it reads\n worst: the message quotes a channel name and calls it a flag.\n */\n let at = -1;\n for (let i = 0; i < argv.length; i++) {\n if (argv[i] !== flag) continue;\n const next = argv[i + 1];\n if (next !== undefined && next.startsWith('-')) {\n at = i;\n break;\n }\n }\n const value = at >= 0 ? argv[at + 1] : undefined;\n if (value === undefined) return asError(raw);\n\n /*\n How the two shapes join, which is not the same and matters.\n\n A long option takes `--out=-nightly`. A short one does NOT take `-o=-nightly`: parseArgs\n reads the equals sign as the first character of the value and hands back \"=-nightly\", so\n that advice would have produced a directory named `=-nightly` without a word — a worse\n failure than the message it replaced, and a silent one. Short options join directly.\n */\n const joined = flag.startsWith('--') ? `${flag}=${value}` : `${flag}${value}`;\n const typeable = survivesBare(joined) ? joined : singleQuoted(joined);\n return printableLines(\n `error: ${flag} was given \"${value}\", which begins with a dash and so reads as another ` +\n `flag rather than as its value.\\n` +\n ` Write it as one argument instead: ${typeable}`,\n );\n}\n\n/** Levenshtein distance, two rows at a time. Twenty names of under twenty characters. */\nfunction editDistance(a: string, b: string): number {\n const row = Array.from({ length: b.length + 1 }, (_unused, i) => i);\n for (let i = 1; i <= a.length; i++) {\n let diagonal = row[0] as number;\n row[0] = i;\n for (let j = 1; j <= b.length; j++) {\n const above = row[j] as number;\n row[j] = Math.min(above + 1, (row[j - 1] as number) + 1, diagonal + (a[i - 1] === b[j - 1] ? 0 : 1));\n diagonal = above;\n }\n }\n return row[b.length] as number;\n}\n\n/**\n * The option they probably meant, or null when nothing is close enough to say.\n *\n * Long options only. A stray `-Z` carries one character of evidence, and guessing a word\n * from it would be guessing.\n *\n * Two rules, because typos come in two shapes. An unfinished name is a prefix — `--chan`,\n * `--decim` — and is only taken from three characters up, so `--c` does not become\n * `--channels` or `--checksum` by coin toss. A misspelt one is within a couple of edits,\n * scaled to the name's length so `--jobs` is not reached from three characters away.\n */\nfunction nearestOption(flag: string): string | null {\n if (!flag.startsWith('--')) return null;\n const typed = flag.slice(2).toLowerCase();\n if (typed.length < 2) return null;\n\n const names = Object.keys(OPTIONS);\n if (typed.length >= 3) {\n const prefixed = names.filter((name) => name.startsWith(typed));\n if (prefixed.length === 1) return `--${prefixed[0] as string}`;\n }\n\n let best: string | null = null;\n let score = Infinity;\n let tied = false;\n for (const name of names) {\n const d = editDistance(typed, name);\n if (d < score) {\n score = d;\n best = name;\n tied = false;\n } else if (d === score) {\n tied = true;\n }\n }\n if (best === null || tied) return null;\n return score <= Math.max(1, Math.min(3, Math.floor(best.length / 3))) ? `--${best}` : null;\n}\n\n/** An option this tool does not have, in a finished sentence, naming the likely one. */\nfunction unknownOption(raw: string): string {\n const found = /^Unknown option '([^']+)'/u.exec(raw);\n // If Node ever rewords it, its text is still true; only the polish is lost.\n if (!found) return raw;\n const flag = found[1] as string;\n const near = nearestOption(flag);\n return printableLines(\n `error: There is no ${flag} option.${near === null ? '' : ` Did you mean ${near}?`}\\n` +\n ` If it is the name of a file, pass it after -- instead:\\n` +\n ` edf2csv -- ${survivesBare(flag) ? `\"${flag}\"` : singleQuoted(flag)}`,\n );\n}\n\nfunction message(error: unknown, indent = ''): string {\n // The indent lines continuation up under an \"error: \" prefix. Usage problems print without\n // one, alongside the usage text, so they pass nothing and stay flush left.\n return printableLines(error instanceof Error ? error.message : String(error), indent);\n}\n\n// `worstOf` is exported for the test that pins the exit-code mapping. A batch's verdict is\n// one of the few things a script branches on, and the mapping is easier to check directly\n// than by racing a signal at a worker.\n// `parseJobs` joins them for the same reason: what `auto` resolves to is a number nothing\n// could see from outside, since the only observable effect is how many children run at once.\nexport { defaultOutputDir, parseJobs, worstOf };\n\n/**\n * Whether this file was executed rather than imported.\n *\n * npm installs a bin as a symlink (node_modules/.bin/edf2csv -> ../edf2csv/dist/cli.js),\n * and that is the path `npx` runs. In that case process.argv[1] is the symlink while\n * import.meta.url is already resolved to the real file, so comparing the two directly\n * reports \"imported\" and the command silently does nothing. Both sides are resolved\n * through realpath before comparing.\n */\nfunction isMainModule(): boolean {\n const entry = process.argv[1];\n if (entry === undefined) return false;\n try {\n return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));\n } catch {\n // An unreadable or deleted entry path is not a reason to refuse to run.\n return pathToFileURL(entry).href === import.meta.url;\n }\n}\n\nconst invokedDirectly = isMainModule();\n\nif (invokedDirectly) {\n main(process.argv.slice(2))\n .then((code) => {\n /*\n A write failure already reported is not erased by a run that thought it finished.\n\n The stdout listener above prints `Writing to stdout failed: ...` and sets EXIT_ERROR,\n and assigning `code` over it put that back to 0 — so `--info > desc.txt` onto a full\n filesystem printed the error, left a zero-byte file, and exited 0. It only looked\n right because the stdout audit threw a second error on the way out, whose message was\n about a short write that had not happened.\n\n A closed pipe deliberately sets no code (see ignoreBrokenPipe), so `--info | head`\n still exits 0 through here.\n */\n process.exitCode = code === EXIT_OK ? (process.exitCode ?? EXIT_OK) : code;\n })\n .catch((error: unknown) => {\n process.stderr.write(`error: ${message(error)}\\n`);\n process.exitCode = EXIT_ERROR;\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * Command-line entry point.\n *\n * Output discipline: anything that is the *result* of a command goes to stdout\n * (`--info`'s channel table, `--json`'s summary). Progress, warnings and the\n * conversion summary go to stderr, so a conversion can be run in a pipeline without\n * its chatter contaminating the data.\n */\n\nimport { parseArgs } from 'node:util';\nimport { pathToFileURL, fileURLToPath } from 'node:url';\nimport { existsSync, realpathSync } from 'node:fs';\nimport { lstat, readdir, realpath, stat } from 'node:fs/promises';\nimport { availableParallelism } from 'node:os';\nimport { fork } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\nimport path from 'node:path';\nimport process from 'node:process';\n\nimport { EdfError } from './edf/errors.js';\nimport { EdfFile } from './edf/reader.js';\nimport { buildPlan, withoutFileRateWarning } from './convert/plan.js';\nimport {\n ConversionError,\n USAGE_ERROR_CODES,\n auditStdout,\n convert,\n defaultOutputDir,\n durationDiagnostics,\n requestedAnnotationWindow,\n noAnnotations,\n noSignalFile,\n stdoutRefusal,\n} from './convert/run.js';\nimport type { ConvertOptions } from './convert/run.js';\nimport { ChannelSelectionError } from './convert/channels.js';\n// Shared with the library so a bad option is the same error whichever way it arrived.\nimport { OptionError } from './convert/options.js';\nimport { TimeRangeError, parseTimeSpec } from './convert/time-range.js';\nimport { deriveRecordStarts, withTimingPromiseKept } from './convert/timing.js';\nimport { formatDiagnostics, formatInfo, infoJson, formatSummary, printable, printableLines, summaryJson, wrap } from './cli/report.js';\nimport { counted, listed } from './format/list.js';\nimport { VERSION } from './version.js';\n\nconst USAGE = `edf2csv ${VERSION}\nConvert EDF, EDF+ and BDF recordings to CSV\n\nUsage\n edf2csv <recording.edf | folder> [more ...] [options]\n\nOptions\n -i, --info Show the recording's structure and the estimated\n output size, without converting anything\n -o, --out <dir> Output directory (default: <recording>_csv, beside\n the input)\n -c, --channels <list> Only these channels, comma-separated. Use #N to pick a\n channel by position when two share a label\n --start <time> Begin at this offset (30s, 5m, 1h30m, 00:30:00)\n --duration <time> Convert this much\n --end <time> Stop at this offset (instead of --duration)\n --annotations-only Write only the EDF+ annotations, no signal data\n --decimals <n> Fix the decimal places instead of deriving them\n per channel\n --checksum Record a SHA-256 of the input in metadata.json\n --layout <kind> wide (default): one column per channel, one file per\n sampling rate. long: one file of time_s,channel,value,\n every rate together, one row per sample\n --gzip Compress every CSV, writing .csv.gz files\n --bom Start each CSV with a UTF-8 byte order mark, so Excel\n reads accented text and units like µV correctly\n -j, --jobs <n> Convert this many recordings at once, or \"auto\"\n (default: 1)\n -f, --force Write into the output directory if it already exists\n -q, --quiet Suppress the summary; warnings and errors still print\n --json Print machine-readable JSON to stdout (works with\n --info too)\n --strict Exit 1 if the recording raised any warning\n --stdout Write the signal CSV to stdout instead of a directory\n (one table only: one sampling rate, or --layout long)\n -h, --help Show this help\n -V, --version Show the version\n\nSeveral recordings\n A folder is expanded to every .edf and .bdf inside it, at any depth, and the\n layout is kept: recordings in sub-folders come out in sub-folders.\n Pass more than one and each is converted in turn. Without --out each lands\n beside itself as usual; with --out that directory becomes the parent and each\n recording gets its own inside it. A file that cannot be read is reported and\n the rest still convert, with a non-zero exit at the end. --jobs converts\n several at once, which is worth it for a folder of them.\n\nOutput\n A directory containing signals.csv, channels.csv, metadata.json, and\n annotations.csv when the recording carries EDF+ annotations. Channels recorded\n at different sampling rates are written to separate files, never resampled.\n With --gzip each CSV becomes a .csv.gz; metadata.json stays plain text so the\n directory can still be read at a glance. With --bom each CSV starts with a\n UTF-8 byte order mark and metadata.json does not, since JSON.parse rejects\n one.\n With --layout long every channel goes into one signals.csv as time_s, channel\n and value, one row per sample, whatever rates the recording mixes — which is\n also the one arrangement --stdout can stream for a mixed-rate file.\n\nExamples\n edf2csv recording.edf\n edf2csv recording.edf --info\n edf2csv recording.edf --channels \"EEG Fpz-Cz,ECG\" --out ./converted\n edf2csv recording.edf --start 30m --duration 5m\n edf2csv recording.edf --annotations-only\n edf2csv /data/*.edf --out ./converted\n edf2csv /data/*.edf --out ./converted --jobs auto\n edf2csv /data/study --out ./converted --jobs auto\n`;\n\n/**\n * A continuation line under an `error: ` or `interrupted (SIGINT): ` head.\n *\n * These are written straight to stderr rather than raised as an error, so they never passed\n * through `printableLines` and never wrapped. Most of them interpolate a destination or a\n * recording path, which makes the line as long as the caller's directory tree is deep: the\n * `--stdout` refusal for a folder reached 268 columns on a four-deep path, saying \"Name the\n * recording itself — <path> — or convert to a directory instead.\"\n *\n * A path has no spaces to break at, so it stays whole on its own line and the sentence\n * around it wraps. That is the useful shape: the path is the part that gets copied.\n */\nconst detail = (text: string): string => `${wrap(text, ' ')}\\n`;\n\n/**\n * Every option the command line takes.\n *\n * Its own constant so `parseArgs` and the \"did you mean\" suggestion read the same list. A\n * second copy of twenty flag names is a copy that will be missing the next one.\n */\nconst OPTIONS = {\n info: { type: 'boolean', short: 'i' },\n out: { type: 'string', short: 'o' },\n channels: { type: 'string', short: 'c', multiple: true },\n start: { type: 'string' },\n duration: { type: 'string' },\n end: { type: 'string' },\n 'annotations-only': { type: 'boolean' },\n decimals: { type: 'string' },\n checksum: { type: 'boolean' },\n layout: { type: 'string' },\n gzip: { type: 'boolean' },\n bom: { type: 'boolean' },\n jobs: { type: 'string', short: 'j' },\n force: { type: 'boolean', short: 'f' },\n quiet: { type: 'boolean', short: 'q' },\n json: { type: 'boolean' },\n strict: { type: 'boolean' },\n stdout: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'V' },\n} as const;\n\nconst EXIT_OK = 0;\nconst EXIT_ERROR = 1;\nconst EXIT_USAGE = 2;\n\n/**\n * Whether this run is already shutting its children down.\n *\n * A child dying by signal is normally worth a line of its own, but not when this process is\n * the one that killed it: the interrupt handler names every abandoned directory in a single\n * message, and a per-child line underneath it would say the same thing again, once per job.\n */\nlet stopping = false;\n\n/**\n * Piping into a consumer that exits early (`| head -1`) closes our stdout, and the\n * next write raises EPIPE. That is normal in a shell pipeline, not an error worth a\n * stack trace, so it is swallowed while anything else still surfaces.\n */\nfunction ignoreBrokenPipe(stream: NodeJS.WriteStream): void {\n stream.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EPIPE') {\n // A reader closing early (`edf2csv ... --info | head -5`) is not a failure, so the\n // error is swallowed rather than thrown. It deliberately does NOT set an exit code:\n // forcing 0 here would erase a real failure whenever the pipe happened to close\n // after the run had already failed, reporting success for a conversion that did\n // not happen. Node exits 0 on its own when nothing sets a code.\n return;\n }\n\n /*\n Anything else is a real write failure, and this used to rethrow it.\n\n The throw lands on a nextTick, outside whatever try/catch the conversion is running\n inside, so it became an uncaught exception: `--stdout` redirected onto a full disk\n died with a raw stack trace headed `dist/cli.js: throw error;` and lost the warning\n that the CSV it had already produced was truncated. The same failure through `--out`\n printed the ordinary message, and so did `--stdout` through the library API, which\n never registers this listener — the designed path exists and works, and this was\n preempting it.\n\n A second listener means the conversion's writer is watching this stream and will\n turn the failure into that ordinary message. Saying it here too would report one\n failure twice, so this only speaks when nothing else will.\n */\n if (stream.listenerCount('error') > 1) return;\n process.stderr.write(`error: Writing to ${stream === process.stdout ? 'stdout' : 'stderr'} failed: ${error.message}\\n`);\n process.exitCode = EXIT_ERROR;\n });\n}\n\nexport async function main(argv: readonly string[]): Promise<number> {\n ignoreBrokenPipe(process.stdout);\n ignoreBrokenPipe(process.stderr);\n\n let values: Record<string, unknown>;\n let positionals: string[];\n\n try {\n const parsed = parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: OPTIONS,\n });\n values = parsed.values as Record<string, unknown>;\n positionals = parsed.positionals;\n } catch (error) {\n process.stderr.write(`${usageMessage(error, argv)}\\n\\nRun edf2csv --help to see the options.\\n`);\n return EXIT_USAGE;\n }\n\n if (values['help'] === true) {\n process.stdout.write(USAGE);\n return EXIT_OK;\n }\n if (values['version'] === true) {\n process.stdout.write(`${VERSION}\\n`);\n return EXIT_OK;\n }\n\n if (positionals.length === 0) {\n process.stderr.write(`No input file given.\\n\\n${USAGE}`);\n return EXIT_USAGE;\n }\n\n /*\n An empty --out, refused here rather than by the filesystem.\n\n Every other option that takes a value refuses an empty one from the command line and says\n what it wanted: `--start is empty`, `--channels was given but lists no channel names`,\n `--layout must be \"wide\" or \"long\", got \"\"`. `--out \"\"` was the eighth of eight and the\n exception — it went the whole way to `mkdir('')` and came back as a conversion failure\n about a path:\n\n error: Cannot create \"\": part of the path does not exist.\n Check the path exists and that you have permission to write there.\n\n Exit 1 where the other seven are exit 2, advice about a path and a permission for a value\n that is neither, and in a batch it is decided once per recording rather than once for the\n run. `--out \"$DEST\"` with `DEST` unset is how it gets written by accident, which is also\n why the empty string alone is refused and the value is not trimmed first: a directory whose\n name is a space is a strange thing to ask for, but it is a thing the filesystem has and a\n path is not a keyword.\n */\n if (values['out'] === '') {\n process.stderr.write('error: --out is empty. Give a directory, for example --out ./converted.\\n');\n return EXIT_USAGE;\n }\n\n let expanded: Input[];\n let unreadable: string[];\n let namedDirectory: boolean;\n try {\n const found = await expandInputs(positionals);\n expanded = found.inputs;\n unreadable = found.unreadable;\n namedDirectory = found.namedDirectory;\n } catch (error) {\n return reportError(error);\n }\n\n // Reported before anything is converted, so it cannot be lost among the summaries, and\n // counted against the run so the exit code does not call a partial sweep a success.\n for (const entry of unreadable) {\n process.stderr.write(\n `error: ${printable(entry)}: could not be read, so any recordings inside it were skipped.\\n`,\n );\n }\n\n if (expanded.length === 0) {\n /*\n \"None here\" and \"could not look\" are different answers, and so are their exit codes.\n\n A folder the process cannot read gave the same exit 2 and the same \"No EDF or BDF\n recordings found\" as an empty one — while the line above it said the folder could not\n be read. Exit 2 is this tool's code for \"the command itself was wrong\", so a script\n was being told to fix its arguments when what needed fixing was a permission. The\n command was fine; the filesystem refused.\n\n And the sentence itself claimed a fact the run is in no position to state: nothing was\n found because nothing was looked at.\n */\n if (unreadable.length > 0) {\n process.stderr.write(\n `${wrap(\n `Nothing could be converted: ${unreadable.length === 1 ? 'that path' : 'those paths'} ` +\n `could not be read, so whether ${unreadable.length === 1 ? 'it holds' : 'they hold'} ` +\n `recordings is unknown.`,\n )}\\n`,\n );\n return EXIT_ERROR;\n }\n process.stderr.write(\n // Escaped, like every other path this prints. These come from the command line, which\n // makes them no safer: a shell glob expands to whatever the directory holds.\n `No EDF or BDF recordings found in ${listed(positionals.map((p) => `\"${printable(p)}\"`))}.\\n`,\n );\n return EXIT_USAGE;\n }\n const inputs = expanded.map((entry) => entry.path);\n /*\n A batch is what you asked for, not what happened to be there.\n\n Counting the recordings made the shape of the run depend on the contents of a folder.\n Under --json a study holding one night printed a pretty-printed object and the same study\n holding two printed two compact lines, so a script written against one of them broke on\n the other — and it broke the day a recording was added, not the day the script changed.\n An input going missing did it in reverse. This is the same count 0.4.20 took out of\n `--out` for the same reason.\n\n Counted from what was named, not from what survived deduplication. `edf2csv one.edf\n alias.edf --out o`, where `alias.edf` is a link to `one.edf`, is two names for one\n recording — converted once, which is right — and the count collapsing to 1 made the run\n stop being a batch: `--out` became the output directory itself, so the files landed in\n `o/` rather than `o/one/`, and `--json` printed one indented document where two\n recordings would have given JSON Lines. cli-reference says of exactly that command that\n it writes `out/one`.\n */\n const batch = positionals.length > 1 || namedDirectory;\n const quiet = values['quiet'] === true;\n const asJson = values['json'] === true;\n const strict = values['strict'] === true;\n const toStdout = values['stdout'] === true;\n\n // Both of these claim stdout. Allowing them together wrote the CSV and then the summary\n // object onto one stream, producing a document that is neither valid CSV nor valid\n // JSON — and silently, since each half looked right on its own.\n /*\n Not under --info, which writes no CSV for --json to collide with.\n\n The two claim stdout because one puts a CSV there and the other a summary. `--info` puts\n neither: it prints a description, and under --json that description *is* the JSON. So\n `--info --stdout --json` — a script asking \"would --stdout work on this recording, in a\n form I can parse\" — was refused for a conflict that cannot arise, which is the same shape\n as the destination guards 0.5.51 stopped applying to it. It is also the only way to see\n the STDOUT_UNSUPPORTED warning from a script.\n */\n if (toStdout && asJson && values['info'] !== true) {\n /*\n `error: ` and the seven-space continuation, like every other refusal.\n\n These two were written before the prefix was, and kept their own shape: every other\n usage error in this tool prints \"error: <what>\" with the advice indented under it, the\n documentation shows them that way, and stderr is what a script greps. A refusal that\n does not match `^error:` is invisible to that grep — and this pair is the one a script\n is most likely to hit, since both flags are things a script passes rather than a person.\n */\n process.stderr.write(\n 'error: --stdout and --json both write to stdout, so they cannot be combined.\\n' +\n ' Use --stdout for the CSV, or --json for the summary.\\n',\n );\n return EXIT_USAGE;\n }\n\n /*\n One stream holds one table, for the same reason it holds one recording: concatenating\n them would give a CSV whose rows come from different files with nothing marking where\n one ends. Naming the count makes it obvious a glob was the cause — except when the count\n is one, which happens for a folder holding a single recording. That read \"--stdout writes\n a single CSV, so it cannot take 1 recordings\": ungrammatical, and wrong on its face, since\n one recording is exactly what it can take. What it cannot take is a folder, whose contents\n are not known until they are walked.\n */\n /*\n Asked of the recordings to stream, not of the run's shape.\n\n These are different questions and 0.5.40 conflated them: it made `batch` count the names\n rather than the recordings, which is right for `--out` and for `--json`, and this guard\n read `batch`. So `edf2csv one.edf one.edf --stdout` — one recording, named twice,\n converted once, perfectly streamable — was refused with the message written for a folder,\n telling the reader to \"name the recording itself\" and quoting the name they had just\n given twice.\n\n A folder is still refused however few recordings it turns out to hold, for the reason\n 0.5.5 gives: what it holds is not known until it is walked.\n */\n if (toStdout && (inputs.length > 1 || namedDirectory)) {\n // Prefixed and indented like the rest; see the --json refusal above. The recording's\n // name goes through `printable` for the reason 0.5.67 gives: a path is untrusted text,\n // and this one is read straight out of a directory the caller named.\n //\n // Quoted where a shell would not hand it back unchanged, by the same rule as the\n // --stdout --gzip hint below, whose comment sets it out: this sentence names a recording\n // to type in place of the folder, and one found inside a folder is as likely to hold a\n // space as the folder is. Unquoted it was two arguments, which --stdout then refuses for\n // being two recordings — the message one step on from the one being read.\n const only = printable(inputs[0] as string);\n process.stderr.write(\n inputs.length === 1\n ? `error: --stdout writes a single CSV, and a folder is converted as a batch even ` +\n `when it holds one recording.\\n` +\n detail(\n `Name the recording itself — ${survivesBare(only) ? only : singleQuoted(only)} — ` +\n `or convert to a directory instead.`,\n )\n : `error: --stdout writes a single CSV, so it cannot take ` +\n `${counted(inputs.length, 'recording')}.\\n` +\n detail('Convert them to directories instead, or run edf2csv once per file.'),\n );\n return EXIT_USAGE;\n }\n\n /*\n Flags that --stdout has nowhere to put.\n\n Both were accepted and dropped in silence. `--out` names a directory that is never\n created, so the run looked like it had written one. `--checksum` is worse than useless:\n the hash is computed before the first record is read, which is a second full pass over\n the input, and then the only file it is ever written to — metadata.json — is not written\n at all. A recording large enough to want a checksum is large enough to notice reading it\n twice for nothing.\n\n Refusing rather than ignoring is what this tool already does for `--stdout --json` and\n `--stdout --annotations-only`.\n */\n /*\n `--force` and `--jobs` were the two left doing nothing in silence.\n\n `--force` means \"write into a directory that already exists\", and there is no directory.\n `--jobs` is deliberately not here. A job count is a property of the run rather than a\n request about the output, `--stdout` clamping it to one is documented, and a wrapper that\n passes `--jobs 4` to everything is not asking for something about this file.\n */\n for (const [flag, given] of [\n ['--out', values['out'] !== undefined],\n ['--checksum', values['checksum'] === true],\n ['--force', values['force'] === true],\n ] as const) {\n if (toStdout && given) {\n // `error:` and the seven-space continuation, like every other refusal — the shape\n // 0.5.79 gave the rest and these two were not enumerated in its test.\n //\n // One sentence on the first line, which is the other half of that shape. The break was\n // written into the middle of it — \"cannot be combined: --stdout writes no\" / \"files,\n // and --out has nothing to act on\" — so the line a log greps carried a fragment ending\n // in \"no\", and the line under it opened with \"files,\". `printableLines` leaves the\n // first line whole at whatever width it runs to, deliberately, so there was nothing to\n // pre-wrap it for.\n process.stderr.write(\n `error: --stdout and ${flag} cannot be combined: --stdout writes no files, and ` +\n `${flag} has nothing to act on.\\n` +\n detail(`Drop ${flag}, or drop --stdout and convert to a directory.`),\n );\n return EXIT_USAGE;\n }\n }\n\n /*\n Compressed bytes at a terminal.\n\n `--stdout --gzip` is a documented pair, and every place it is documented redirects it:\n `edf2csv rec.edf --stdout --gzip > signals.csv.gz`. Without the redirect it wrote the\n deflate stream to the terminal — 46 control bytes and three ESCs out of 244 for the\n smallest fixture here, which is a terminal left in whatever state those happened to\n describe, and nothing usable on the screen either way.\n\n This tool escapes every control byte out of a header before printing it, on the reasoning\n that a file should not be able to drive the reader's terminal. Doing it to them directly,\n from output it generated itself, is the same hazard with the argument for it removed.\n\n Only when stdout is a terminal. A pipe and a regular file are both `isTTY === false`, so\n the documented command is unaffected — and that is also why this cannot be answered by\n refusing the flag pair outright, the way `--stdout --json` is.\n\n gzip itself has declined to write compressed data to a terminal for thirty years, which\n is where a reader's expectation comes from.\n */\n if (toStdout && values['gzip'] === true && process.stdout.isTTY === true) {\n /*\n `<recording>` is not a placeholder a shell leaves alone.\n\n This line is printed on its own, indented, in the shape every command this tool offers\n is printed in — and 0.7.18 and 0.7.19 went through the others making sure each one can\n be pasted. Pasted, `<recording>` is a redirect: the shell looks for a file called\n `recording` and the command never runs, or one exists and edf2csv is handed a recording\n on stdin, which it does not read. Either way the answer to \"what do I type instead\"\n does not survive being typed.\n\n The name is in the invocation being refused, and exactly one of them reaches here: a\n folder and a second recording are both turned away above. Quoted by the same rule as\n the other two hints, so a path with a space in it stays one argument.\n */\n const named = printable(inputs[0] as string);\n process.stderr.write(\n 'error: --stdout --gzip would write compressed bytes straight to the terminal.\\n' +\n detail('Redirect it to a file or a pipe:') +\n ` edf2csv ${survivesBare(named) ? named : singleQuoted(named)} ` +\n `--stdout --gzip > signals.csv.gz\\n`,\n );\n return EXIT_USAGE;\n }\n\n /*\n One recording prints the document it always printed; several print one per line.\n\n Pretty-printed objects run together are still readable by a streaming JSON parser, but\n not by anything that reads a record per line — and a batch is exactly where that is\n wanted. Emitting a batch in the single-file shape would repeat 0.2.28's mistake of\n putting two documents on one stream and leaving the caller to work out the boundary.\n\n null, not undefined: the serialisers default this argument to 2, and a default parameter\n takes effect for undefined, which silently gave a batch the indentation it was supposed\n to be dropping.\n */\n const jsonIndent = batch ? null : 2;\n\n try {\n const outOption = typeof values['out'] === 'string' ? values['out'] : undefined;\n const destinations = destinationsFor(expanded, outOption, batch);\n /*\n Where the output would land is not --info's problem to refuse over. It is still\n --info's job to mention it.\n\n 0.5.32 stopped the guards refusing `--info`, and left nothing in their place — while\n the sentence it added to cli-reference says \"`--info --out` is how you would want to\n find out about them\". So the one command documented as the way to learn about a\n collision said nothing at all about it. Reported as a warning now, using the guard's own\n message so there is one wording rather than two.\n */\n if (values['info'] === true) {\n try {\n assertDistinct(inputs, destinations);\n } catch (error) {\n if (!(error instanceof OptionError)) throw error;\n process.stderr.write(`warning: ${printableLines(error.message, ' ')}\\n`);\n }\n }\n\n /*\n Where the output would land is not --info's problem, because --info has no output.\n\n Both guards refused it: `edf2csv study --info --out yy` on a folder holding `rec.edf`\n beside `rec/inner.edf` exited 2 with \"would be converted into yy/rec/inner, which is\n inside yy/rec — where rec.edf is converted\", and printed nothing about either\n recording. Two recordings whose names collide got the overwrite refusal the same way.\n Both messages assert a conversion and an overwrite that --info does not perform, and\n the identical command without --out describes both files happily.\n\n Which makes the refused command the useful one: `--info --out` is how you ask what a\n run would produce before committing to it, and a collision is exactly the thing you\n would want it to tell you about rather than refuse to look.\n */\n if (values['info'] !== true) assertDistinct(inputs, destinations);\n\n const shared = {\n channels: splitChannels(values['channels']),\n start: optionalTime(values['start'], '--start'),\n startText: typeof values['start'] === 'string' ? values['start'] : undefined,\n duration: optionalTime(values['duration'], '--duration'),\n end: optionalTime(values['end'], '--end'),\n endText: typeof values['end'] === 'string' ? values['end'] : undefined,\n decimals: optionalDecimals(values['decimals']),\n annotationsOnly: values['annotations-only'] === true,\n gzip: values['gzip'] === true,\n bom: values['bom'] === true,\n layout: optionalLayout(values['layout']),\n };\n\n // Validated before the --info branch, not inside the conversion path: a flag that\n // cannot be honoured is a usage error whatever mode it was given in, and accepting\n // \"--jobs 0\" in silence under --info is the kind of quiet that this tool avoids.\n // Parsed before it is overridden: --stdout converts one recording however many jobs\n // were asked for, but \"--stdout --jobs 0\" is still a request that cannot be honoured,\n // and accepting it in silence is the thing 0.4.2 fixed for --info.\n const requestedJobs = parseJobs(values['jobs'], inputs.length);\n /*\n Asking for several at once, of a mode that converts one.\n\n After `parseJobs`, not before it: a malformed count is a fact about the value and gets\n the message about the value, which is what the check above this one has always done and\n what its own comment insists on — \"a request that cannot be met is a usage error rather\n than something to accept in silence\". So `--stdout --jobs 0x10` still reports the `0x10`.\n\n `--jobs 1` is not refused. It asks for precisely what happens, and a script passing it\n uniformly is not asking for anything it will not get; `auto` resolves to 1 on one\n recording. What is refused is a number greater than one, which was accepted and dropped.\n */\n const jobs = toStdout ? 1 : requestedJobs;\n\n if (values['info'] === true) {\n const failures: number[] = [];\n let warnings = 0;\n for (const [index, input] of inputs.entries()) {\n // A blank line between reports, so several tables read as one document.\n if (batch && !asJson && index > 0) process.stdout.write('\\n');\n try {\n warnings += await showInfo(\n input as string,\n shared,\n asJson,\n jsonIndent,\n batch,\n values['stdout'] === true,\n );\n } catch (error) {\n failures.push(reportError(error, batch ? (input as string) : undefined));\n }\n }\n if (unreadable.length > 0) failures.push(EXIT_ERROR);\n if (failures.length > 0) return worstOf(failures);\n /*\n And it says that --strict is why, as a conversion does.\n\n This returned the code and printed nothing. Exit 1 out of `--info` otherwise means the\n recording could not be read, so a screening script — the use this combination is\n documented for, \"a cheap way to screen a directory before anyone converts them\" —\n could not tell a broken file from one that merely raised a warning. Under `--json` it\n was worse: a well-formed report on stdout, an empty stderr, and a 1.\n\n The sentence the conversion path writes is the one that was missing, minus its closing\n clause: there is no output to say was still written.\n */\n if (strict && warnings > 0) {\n process.stderr.write(\n `\\n${wrap(\n `--strict: ${warnings} warning${warnings === 1 ? '' : 's'} raised, so this run is ` +\n `reported as a failure. Nothing was written either way, since --info converts nothing.`,\n )}\\n`,\n );\n return EXIT_ERROR;\n }\n return EXIT_OK;\n }\n\n // The meter redraws one line in place, which two conversions cannot share. Running them\n // at once replaces it with the completion count each file prints as it lands.\n const showProgress =\n !quiet && !asJson && !toStdout && jobs === 1 && process.stderr.isTTY === true;\n\n const failures: number[] = [];\n let warnings = 0;\n let converted = 0;\n\n /** Convert input `index`, sending its output wherever the caller wants it. */\n const runOne = async (index: number, emit: Emit): Promise<void> => {\n const input = inputs[index] as string;\n const destination = destinations[index] as string;\n // Which recording this is, before anything it prints. Without it a batch produces a\n // stack of summaries and errors with nothing saying which file each belongs to.\n if (batch && !quiet && !asJson) {\n emit('err', `[${index + 1}/${inputs.length}] ${printable(input)}\\n`);\n }\n try {\n warnings += await convertOne(\n input,\n destination,\n {\n ...shared,\n // With one input and no --out, convert() derives the default itself, exactly as\n // it did before batches existed.\n outputDir: outOption === undefined && !batch ? undefined : destination,\n checksum: values['checksum'] === true,\n force: values['force'] === true,\n toStdout,\n quiet,\n asJson,\n showProgress,\n jsonIndent,\n batch,\n },\n emit,\n );\n converted++;\n } catch (error) {\n /*\n A batch keeps going. One unreadable recording among five hundred is a reason to\n report that file, not to abandon the ones already converted and refuse the rest.\n The exit code still reports the run as failed, and the closing line says how many\n of them made it, so nothing about the failure is quiet.\n */\n failures.push(reportError(error, batch ? input : undefined, emit));\n }\n };\n\n if (jobs === 1) {\n for (let index = 0; index < inputs.length; index++) {\n await runOne(index, writeThrough);\n // A single recording keeps the exit code it has always had rather than the batch's.\n if (!batch && failures.length > 0) return failures[0] as number;\n }\n } else {\n /*\n Real processes, not concurrent promises.\n\n Converting is almost entirely arithmetic and string building — 1.17 s of CPU for\n 1.24 s of wall clock on a 168 MB conversion — and Node runs that on one thread. An\n in-process pool was tried first and gained 6% on eight recordings, which is the\n overlap in the file reads and nothing else. Each conversion is already a whole\n command, so each one gets its own process, which is what `xargs -P` would do by hand.\n\n Workers take the next recording as they free up rather than splitting the list into\n equal shares. Recordings in a folder differ wildly in length, and a fixed split\n leaves every worker but one idle behind the longest file.\n\n Each child's output is held until it exits, so two finishing together cannot\n interleave one's summary with the other's warnings.\n */\n const running = new Map<ChildProcess, string>();\n\n /*\n Ctrl-C in a terminal reaches every process in the group, so the children would stop\n anyway; a signal sent to this process alone does not, and that is how a batch gets\n run from a script or a CI job. Interrupting one left four conversions writing\n gigabytes into a directory their owner believed abandoned, and the last thing on\n screen was a successful \"Done in 1.6s\" from whichever recording had just landed —\n the run read as if it had finished.\n */\n const onInterrupt = (signal: NodeJS.Signals): void => {\n stopping = true;\n const abandoned = [...running.values()];\n for (const child of running.keys()) child.kill('SIGTERM');\n /*\n A destination that is not there was never written to, and saying otherwise is the\n defect the serial handler was fixed for.\n\n `convert()` opens the recording, hashes it under `--checksum` and reads the whole\n annotation channel for record start times *before* it claims the directory — so\n Ctrl-C in the first seconds of a batch of large EDF+ recordings finds nothing on\n disk for any of them. This named every destination as incomplete output regardless,\n and advised distrusting directories `ls` then reported did not exist.\n\n Two answers rather than the serial path's three: whether a directory that IS there\n was there before this run is per-child state the parent does not keep, and the\n sentence that covers both is the one already printed.\n */\n const started = abandoned.filter((destination) => existsSync(destination));\n const untouched = abandoned.filter((destination) => !existsSync(destination));\n process.stderr.write(\n `\\ninterrupted (${signal}): ${abandoned.length} conversion` +\n `${abandoned.length === 1 ? '' : 's'} stopped part way through.\\n` +\n (started.length > 0\n ? detail(`Incomplete, and should not be used: ${listed(started.map(printable))}`)\n : '') +\n (untouched.length > 0\n ? detail(\n `Nothing was written to ${listed(untouched.map(printable))}: ` +\n `${untouched.length === 1 ? 'that directory was' : 'those directories were'} ` +\n `never created.`,\n )\n : ''),\n );\n process.exit(signal === 'SIGINT' ? 130 : 143);\n };\n process.once('SIGINT', onInterrupt);\n process.once('SIGTERM', onInterrupt);\n\n let next = 0;\n const worker = async (): Promise<void> => {\n for (let index = next++; index < inputs.length; index = next++) {\n const input = inputs[index] as string;\n const sink = buffered();\n if (!quiet && !asJson) {\n sink.emit('err', `[${index + 1}/${inputs.length}] ${printable(input)}\\n`);\n }\n const child = await convertInChild(\n input,\n destinations[index] as string,\n values,\n running,\n );\n // The report says what the child actually did; the exit code says whether it\n // got there. A child that converted and warned has a report and exits 0, since\n // --strict is the parent's to apply.\n if (child.report) {\n converted += child.report.converted;\n warnings += child.report.warnings;\n } else if (child.code === 0) {\n converted++;\n }\n if (child.code !== 0) failures.push(child.code);\n // The child saw one recording, so it printed the indented document a single\n // conversion prints. A batch is one object per line.\n sink.emit('out', asJson ? compactJson(child.out) : child.out);\n /*\n The child converted a single recording, so it named no file in its errors the way\n a batch does. Naming it here keeps the two paths identical to a reader and to\n anything grepping a log, where the [n/m] header may not be alongside.\n\n Warnings too, under --quiet, and for exactly the reason 0.5.49 gave for the serial\n path: the `[n/m] <path>` header is what pairs a warning with the file that raised\n it, --quiet suppresses that header, and it took the attribution with it. That fix\n keyed off the child's own `batch` flag, which a forked child does not have — it\n was handed one recording and one destination, so it believes it is a single\n conversion and says nothing. Two recordings, two warnings, no way to tell which\n raised which, and under --jobs not even a stable order to guess from.\n\n Not when the header is printed, or every warning would carry the name twice.\n */\n sink.emit('err', named(child.err, input, quiet));\n sink.flush();\n }\n };\n try {\n await Promise.all(Array.from({ length: jobs }, () => worker()));\n } finally {\n process.off('SIGINT', onInterrupt);\n process.off('SIGTERM', onInterrupt);\n }\n }\n\n /*\n A path that could not be read is a failure of this run, and the closing line has to\n count it before it prints rather than after.\n\n It was counted afterwards, so a folder holding one recording beside a sub-directory\n without read permission printed \"Converted 1 of 1 recordings.\" and exited 1. The line\n agreed with itself and with nothing else — which is the failure the walk's own comment\n says was fixed, quoting that very sentence. What was fixed then was the error line and\n the exit code; the summary went on saying everything worked.\n\n Counted separately from the recordings, because an unreadable path is not one of them:\n how many recordings it held is the thing nobody knows.\n */\n if (unreadable.length > 0) failures.push(EXIT_ERROR);\n\n if (batch && !quiet && !asJson) {\n const unread =\n unreadable.length > 0\n ? `; ${unreadable.length} path${unreadable.length === 1 ? '' : 's'} could not be read`\n : '';\n const failed = converted < inputs.length ? `; ${inputs.length - converted} failed` : '';\n process.stderr.write(\n `\\nConverted ${converted} of ${counted(inputs.length, 'recording')}${failed}${unread}.\\n`,\n );\n }\n\n /*\n A parent that forked this process needs the counts, not just an exit status.\n\n An exit code cannot separate \"converted, and raised warnings\" from \"did not convert\",\n and under --strict those are the same code. The parent read it as a failure, so a\n parallel run of two recordings — one of which merely warned — reported \"Converted 1 of\n 2 recordings; 1 failed\" for a run in which both converted, while the serial path said\n \"Converted 2 of 2\". `process.send` exists only when this process was forked with a\n channel, so nothing changes for an ordinary invocation.\n */\n process.send?.({ edf2csv: { converted, warnings } });\n\n if (failures.length > 0) return worstOf(failures);\n\n /*\n --strict turns any warning into a non-zero exit, for pipelines that would rather stop\n than proceed on a recording the tool had something to say about.\n\n The output is still written. A warning describes the recording, not a failure to\n convert it — a truncated file converts correctly for the records that are there — so\n destroying that work would be the wrong response. The exit code is the signal; what\n to do about it is the caller's decision, and they still have the files to inspect.\n */\n if (strict && warnings > 0) {\n process.stderr.write(\n `\\n${wrap(\n `--strict: ${warnings} warning${warnings === 1 ? '' : 's'} raised, so this run is ` +\n `reported as a failure. The output was still written.`,\n )}\\n`,\n );\n return EXIT_ERROR;\n }\n return EXIT_OK;\n } catch (error) {\n return reportError(error);\n }\n}\n\n/** Print one recording's `--info`, and return how many diagnostics it raised. */\nasync function showInfo(\n input: string,\n shared: Record<string, unknown>,\n asJson: boolean,\n jsonIndent: number | null,\n batch = false,\n toStdout = false,\n): Promise<number> {\n const file = await EdfFile.open(input);\n try {\n /*\n Read the annotation channel only when the timing actually depends on it.\n\n --info is documented as a header-only summary that returns immediately whatever\n the file's size, but it called readAnnotations() on every EDF+/BDF+ file — a seek\n into every data record. deriveRecordStarts discards that data unless the file is\n EDF+D, where record start times are stored rather than arithmetic, so on a\n continuous recording the whole scan was thrown away. It cost 0.29 s on a 12 MB\n file and scaled with record count.\n\n A discontinuous file still needs the scan: without it the reported span and row\n estimate are wrong, which is a bug that has already been fixed once here.\n */\n const hasAnnotations = file.annotationSignals.length > 0;\n const needsEveryRecordStart = file.header.continuity === 'EDF+D' && hasAnnotations;\n /*\n A continuous recording needs its origin too, which is one read rather than one per\n record: 0.4.9 made its first record's timekeeping TAL the point the samples are timed\n from, and this report went on placing a requested window against zero. `--info --start 1`\n predicted 8 rows where the conversion wrote 10, on a file whose discontinuous twin —\n identical but for the reserved field — agreed with itself.\n */\n /*\n And a file with an annotation channel and no marker at all, which is the one case this\n condition used to exclude and the one that most needs it.\n\n Without `EDF+C` or `EDF+D` the origin is not applied — the samples are timed from zero —\n but the annotation channel is found by label, so its events keep the onsets the file\n gives them and the two CSVs come out on clocks the origin apart. That is what\n MISSING_EDF_PLUS_MARKER says, and it is raised from the record starts, which this\n declined to read for exactly the files that produce it. So a conversion warned that\n signals.csv and annotations.csv would be a hundred seconds apart, and `--info` — run\n first, on purpose, to find out what a conversion would say — said nothing at all.\n\n The same single read the continuous case already pays for, on a file that is anomalous\n to begin with: an EDF Annotations channel is not something a plain EDF file has.\n */\n const scan =\n !needsEveryRecordStart && file.header.continuity !== 'EDF+D' && hasAnnotations\n ? await file.scanOrigin()\n : null;\n const annotationData = needsEveryRecordStart\n ? await file.readAnnotations()\n : {\n annotations: [],\n recordStarts: scan ? [scan.origin] : [],\n malformed: scan?.malformed ?? 0,\n // Counted rather than assumed: see EdfFile.scanOrigin.\n malformedTimekeeping: scan?.malformedTimekeeping ?? 0,\n // And how many of those took events with them, which decides whether the warning\n // beside it says nothing was lost. See EdfFile.scanOrigin.\n malformedTimekeepingWithText: scan?.malformedTimekeepingWithText ?? 0,\n };\n const timing = deriveRecordStarts(file, annotationData);\n const plan = buildPlan(\n {\n signals: file.header.signals,\n recordDuration: file.header.recordDuration,\n recordCount: file.recordCount,\n hasAnnotationChannel: file.annotationSignals.length > 0,\n recordStarts: timing.starts,\n },\n shared,\n );\n /*\n The event count, where reading the channel has already produced it.\n\n Only a discontinuous file, where every record start has to be read out of the annotation\n channel; a continuous one is read as far as the first record that states a start time and\n no further, so its events are not all in hand. Counted through the same window predicate\n a conversion filters them by, rather than a second copy of it.\n */\n const eventWindow = requestedAnnotationWindow(shared, plan.range.recordingStartSeconds);\n const knownEvents = needsEveryRecordStart\n ? annotationData.annotations.filter(\n (a) => a.onset >= eventWindow.from && a.onset < eventWindow.to,\n ).length\n : null;\n plan.diagnostics.push(...timing.diagnostics);\n /*\n What --stdout would do with this recording, which --info did not ask.\n\n `--info --stdout` on a three-rate file predicted \"Would write 1,155 rows, roughly\n 22.2 KB\" and said the channels \"are written to one file per rate\" — for a command that\n refuses to run, writes nothing and names no file. --info exists to say what a\n conversion will do, and refusing is one of the things it does.\n\n A warning rather than a refusal, for the reason 0.5.51 gives about the destination\n guards: --info writes nothing, so a rule about the output has no business stopping it\n from describing the recording — and being told the command will not work is exactly\n what was asked. The conversion's own guard supplies the words, so there is one wording.\n */\n /*\n What the conversion would say about the durations it would write.\n\n Same window the conversion applies, so --info predicts the warnings rather than a\n different set. Raised here rather than in the parser because the counts have to be of\n the rows that reach annotations.csv — see durationDiagnostics.\n\n `eventWindow` above and not the resolved range, which is what this passed. The two are\n the same window only while the conversion asks for no more than the recording holds:\n the range is clamped to the recording, and an annotation onset is not obliged to fall\n inside it — a marker for the end of a recording sits at exactly its length, and\n annotations-at-edges.edf exists because filtering events by the range dropped those.\n So a two-second EDF+D carrying one unreadable duration inside its span and one past the\n end was described by\n\n --info 1 annotation states a duration that is not a number, so its ...\n conversion 2 annotations state a duration that is not a number, so their ...\n\n about the same file with no time option given, over an annotations.csv holding both\n rows. `knownEvents` three lines up already counts through `eventWindow`, for the reason\n its comment gives; this was the second copy that comment says not to make.\n */\n plan.diagnostics.push(...durationDiagnostics(annotationData.annotations, eventWindow));\n\n /*\n And the warning about the signal file this conversion would not write.\n\n `--info` prints one accurate line about it in the report body — \"Would write\n channels.csv and no signal data\" — and raised nothing, so the one mode whose purpose is\n to say what a conversion will do carried a shorter warning list than the conversion did.\n `--info --strict` is documented as a cheap way to screen a directory before converting\n it, and on a recording of nothing but annotations it reported one warning where the\n conversion reports two.\n */\n const missing = noSignalFile(file, plan);\n if (missing) plan.diagnostics.push(missing);\n // The same for --annotations-only on a file with none; see noAnnotations.\n const noEvents = noAnnotations(file, shared as ConvertOptions);\n if (noEvents) plan.diagnostics.push(noEvents);\n\n if (toStdout) {\n const refusal = stdoutRefusal(file, plan);\n if (refusal) {\n plan.diagnostics.push({\n code: 'STDOUT_UNSUPPORTED',\n severity: 'warning',\n message: `--stdout would refuse this recording: ${refusal.message.replace(/^--stdout /u, '')}`,\n ...(refusal.hint === undefined ? {} : { hint: refusal.hint }),\n });\n }\n }\n /*\n Checked, like a conversion's stdout is.\n\n This wrote and looked at nothing, so `--info > desc.txt` into a filesystem with no room\n produced a zero-byte file and exited 0. The same audit the `--stdout` path uses: it\n declines anything that is not a regular file, so a pipe or a terminal is unaffected, and\n `--info | head` keeps exiting 0.\n */\n const audit = auditStdout();\n const description = asJson\n ? `${infoJson(file, plan, jsonIndent)}\\n`\n : `${formatInfo(file, plan, knownEvents)}\\n`;\n process.stdout.write(description);\n audit?.count(Buffer.byteLength(description));\n audit?.verify();\n\n // Under --json the warnings travel inside the document, exactly as they do for a\n // conversion, so stderr stays empty and the whole result is one parseable thing.\n const diagnostics = [...withTimingPromiseKept(withoutFileRateWarning(file.diagnostics), timing.starts !== null), ...plan.diagnostics];\n if (!asJson && diagnostics.length > 0) {\n /*\n Named when there is more than one recording to confuse it with.\n\n The table goes to stdout and the warnings to stderr, which is the point of the split\n — but over a folder that left several warnings in a row on stderr with nothing saying\n which recording raised any of them. Two recordings, two warnings, and no way to pair\n them up short of running the tool again one file at a time. A batch conversion has\n named its recordings since 0.4.20; this is the same report from the same tool about\n the same files.\n */\n const where = batch ? `${printable(input)}: ` : '';\n process.stderr.write(\n `\\n${formatDiagnostics(diagnostics).replace(/^(warning|note): /gmu, (m) => `${m}${where}`)}\\n`,\n );\n }\n return diagnostics.length;\n } finally {\n await file.close();\n }\n}\n\n/** Convert one recording, and return how many diagnostics it raised. */\nasync function convertOne(\n input: string,\n destination: string,\n options: Record<string, unknown> & {\n quiet: boolean;\n asJson: boolean;\n showProgress: boolean;\n toStdout: boolean;\n jsonIndent: number | null;\n batch?: boolean;\n },\n emit: Emit = writeThrough,\n): Promise<number> {\n const { quiet, asJson, showProgress, toStdout } = options;\n let lastTick = 0;\n\n /*\n Interrupting a conversion leaves a CSV that stops mid-recording but is still\n perfectly well-formed, so nothing about the file itself reveals that half the\n data is missing. Saying so on the way out is the whole point of a tool that\n claims it will not go quiet when something is wrong.\n */\n /*\n Whether the destination was already there before this run touched it.\n\n Read once, up front, because at interrupt time it is the difference between three\n different true statements and there is no other way to tell them apart. A directory that\n exists now and did not exist then was created by this conversion; one that existed then\n holds files this conversion may never have reached.\n */\n const destinationExistedBefore = toStdout ? false : existsSync(destination);\n\n /*\n Take the progress meter off the line before anything else is printed on it.\n\n The meter writes `\\r converting\\u2026 47%` and leaves the cursor after the percentage, so\n whatever is written next continues that line. Success cleared it and an interrupt cleared\n it; a conversion that *failed* did not, and the error landed on the end of the meter:\n\n converting\\u2026 96%error: Expected 317440 bytes of data at record 1638 but only 0 ...\n\n Which is the one thing this output is shaped to avoid. Every release since 0.7.1 has kept\n `error: ` and `warning: ` at the start of a line so a batch's stderr can be grepped for\n them, and here the prefix sat mid-line, where `grep \\'^error:\\'` over a failed run finds\n nothing at all. Only reachable on a terminal, which is why nothing caught it: the meter is\n off whenever stderr is not a TTY, and a captured stderr never is.\n */\n const clearProgress = (): void => {\n if (showProgress) process.stderr.write('\\r\\u001b[K');\n };\n\n const onInterrupt = (signal: NodeJS.Signals): void => {\n clearProgress();\n /*\n Say which of the three happened, rather than the middle one every time.\n\n --stdout writes no directory, so there is none to warn about. Naming one anyway\n pointed at a path that was never created — the same \"files that were never written\"\n that 0.2.30 removed from this path's error message. The directory case had the same\n defect one step further in: `convert()` hashes the input under --checksum and scans\n the whole annotation channel for record start times *before* it claims the directory,\n so a Ctrl-C in the first second of a large EDF+ printed \"Files already written to\n \"oa\" are incomplete and should not be used\" about a directory that `ls` then reported\n did not exist. Nothing had been written, and the advice was to distrust nothing.\n */\n const wrote = !toStdout && existsSync(destination);\n process.stderr.write(\n `\\ninterrupted (${signal}): the conversion stopped part way through.\\n` +\n (toStdout\n ? detail('The CSV on stdout stops mid-recording and should not be used.')\n : !wrote\n ? detail(`Nothing was written: \"${printable(destination)}\" was never created.`)\n : destinationExistedBefore\n ? detail(\n `\"${printable(destination)}\" was already there, so what is in it may be ` +\n `this run's incomplete output.`,\n )\n : detail(\n `Files already written to \"${printable(destination)}\" are incomplete and ` +\n `should not be used.`,\n )),\n );\n // 128 + signal number, the conventional exit status for dying to a signal.\n process.exit(signal === 'SIGINT' ? 130 : 143);\n };\n process.once('SIGINT', onInterrupt);\n process.once('SIGTERM', onInterrupt);\n\n try {\n const result = await convert(input, {\n ...options,\n onProgress: showProgress\n ? (progress): void => {\n const now = Date.now();\n if (now - lastTick < 100) return;\n lastTick = now;\n const percent =\n progress.recordsTotal === 0\n ? 100\n : Math.floor((progress.recordsDone / progress.recordsTotal) * 100);\n process.stderr.write(`\\r converting… ${percent}%`);\n }\n : undefined,\n });\n\n clearProgress();\n\n if (result.diagnostics.length > 0 && !asJson) {\n /*\n Named when nothing else is naming it.\n\n A batch prints a `[n/m] <path>` header before each recording, and that header is what\n pairs a warning with the file it came from. `--quiet` suppresses it — it is the\n summary line it is documented to suppress, and it took the attribution with it. Two\n recordings, two warnings, and no way to tell which raised which, while `error:` lines\n in the same run stay named because 0.4.20 prefixes those separately.\n\n Same fix 0.5.34 made for `--info` over a folder, which has no header to lose.\n */\n const where = options.batch === true && quiet ? `${printable(input)}: ` : '';\n /*\n The trailing blank line separates these warnings from the summary underneath them,\n so it belongs to the summary and goes when the summary does.\n\n `--quiet` printed it anyway: one stray blank line per recording, in the mode whose\n entire purpose is to print less. On a batch of five hundred that is five hundred of\n them in the log, and on a single recording it left `--quiet --strict` showing two\n blank lines between the warning and the \"--strict:\" line where the ordinary run\n shows one.\n */\n emit(\n 'err',\n `${formatDiagnostics(result.diagnostics).replace(/^(warning|note): /gmu, (m) => `${m}${where}`)}\\n${quiet ? '' : '\\n'}`,\n );\n }\n if (asJson) {\n // One object per line, so a batch is JSON Lines — `jq` reads a record at a time\n // rather than waiting for the whole run to finish.\n emit('out', `${summaryJson(result, options.jsonIndent as number | null)}\\n`);\n } else if (!quiet) {\n // With --stdout there is no directory to summarise, and the row count is the only\n // thing worth saying — on stderr, so the CSV on stdout stays clean.\n if (toStdout) {\n const rows = result.files[0]?.rows ?? 0;\n /*\n A reader that closed the pipe did not receive a conversion, so it does not get a\n conversion's summary. `edf2csv rec.edf --stdout | head -1` announced \"Wrote 52,507\n rows to stdout\" — a number that is neither the recording's 102,400 nor the one row\n head took, but however many had been formatted before the close was noticed.\n\n Whether the *conversion* stopped early is a separate question from whether the\n reader did, and 0.5.12 answered the first with the second. A 10,000-row recording\n whose CSV outruns the pipe buffer but fits one flush is written in full and only\n then meets the closed pipe: every row formatted, every row handed over, and the\n summary said \"The recording was not converted in full.\" The estimate's row count is\n exact, so the two cases can be told apart and told apart honestly — what reached\n the reader is not knowable from this side either way.\n */\n const expected = result.plan.estimate.rows;\n // Singular at one, like every other count. A window narrow enough to select a single\n // sample is an ordinary thing to pipe, and this said \"Wrote 1 rows to stdout.\" — the\n // third message in this family, after 0.5.74 and 0.5.78, each found because the sweep\n // that checks for it was narrower than the set of modes that print a count.\n const written = `${rows.toLocaleString('en-US')} ${rows === 1 ? 'row' : 'rows'}`;\n emit(\n 'err',\n !result.readerHungUp\n ? `Wrote ${written} to stdout.\\n`\n : rows < expected\n ? `Stopped: the reader closed the pipe after ${rows.toLocaleString('en-US')} of ` +\n `${expected.toLocaleString('en-US')} ${expected === 1 ? 'row' : 'rows'} had been ` +\n `written. The recording was ` +\n `not converted in full.\\n`\n : `Wrote ${written} to stdout, but the reader closed ` +\n `the pipe before the end, so not all of them reached it.\\n`,\n );\n } else {\n emit('err', `${formatSummary(result)}\\n`);\n }\n }\n return result.diagnostics.length;\n } catch (error) {\n // The meter is still on the line when a conversion throws, and the caller's `error: `\n // is the next thing written.\n clearProgress();\n throw error;\n } finally {\n process.off('SIGINT', onInterrupt);\n process.off('SIGTERM', onInterrupt);\n }\n}\n\n/** A recording to convert, and the path its output should be named after. */\ninterface Input {\n /** Where the recording is. */\n path: string;\n /**\n * What to call its output, relative to `--out`.\n *\n * For a file named on the command line this is just its own name. For one found by\n * expanding a directory it keeps the position it had inside that directory, so a study\n * laid out as one folder per night comes out the same shape — and, more to the point, so\n * that fifty recordings all named `rec.edf` do not all claim `<out>/rec`.\n */\n name: string;\n /**\n * Whether this recording was found by expanding a directory rather than named directly.\n *\n * `--out` means two different things — the output directory itself, or a parent to put one\n * directory per recording inside — and this is what decides which. See `destinationsFor`.\n */\n fromDirectory: boolean;\n}\n\n/**\n * Recordings to convert, with directories expanded to what is inside them.\n *\n * A directory yields every `.edf` and `.bdf` beneath it, at any depth. Recordings arrive\n * organised into folders, and a shell has no tidy way to reach them — which is why the\n * recipes here carried a `find` incantation to do it. Passing the folder is the obvious\n * thing to try, and it used to fail with \"is a directory, not an EDF file\".\n */\nasync function expandInputs(\n positionals: readonly string[],\n): Promise<{ inputs: Input[]; unreadable: string[]; namedDirectory: boolean }> {\n const found: Input[] = [];\n const unreadable: string[] = [];\n /*\n Whether a directory was NAMED, which is not the same as whether one yielded anything.\n\n `fromDirectory` on the inputs answers the second question, and using it for the first put\n the 0.4.20 defect back one step along: `edf2csv study named.edf --out csv` wrote\n csv/named/ while the study held recordings and csv/signals.csv once it held none, because\n the surviving input then looked like a lone file. Whether some unrelated folder happens\n to contain anything decided where a different recording's output went.\n */\n let namedDirectory = false;\n for (const given of positionals) {\n const info = await stat(given).catch(() => null);\n if (info === null || !info.isDirectory()) {\n // Anything that is not a directory is passed through untouched, so a file that does\n // not exist still reports itself rather than vanishing from the list.\n found.push({ path: given, name: path.basename(given), fromDirectory: false });\n continue;\n }\n namedDirectory = true;\n const walked = await walk(given);\n unreadable.push(...walked.unreadable);\n for (const file of walked.files) {\n found.push({ path: file, name: path.relative(given, file), fromDirectory: true });\n }\n }\n /*\n One recording, however many ways it was named.\n\n The walk already does this for links inside a folder, and the two halves disagreed:\n `edf2csv study` converted a recording reached twice once, while `edf2csv a.edf a.edf`\n refused the whole run for the collision it would cause. A shell makes the second easy\n to produce by accident — `edf2csv *.edf recording.edf` — and there is nothing ambiguous\n about it: it is one recording, and converting it once is what was meant.\n\n A path that does not resolve keeps its own identity so that a file which is not there\n still reports itself rather than being folded into another entry.\n\n Which of the names survives is decided by the names themselves, not by the order they\n turned up in. Keeping the first arrival meant the output directory was named by argument\n order — `edf2csv data/one.edf data/alias.edf` wrote out/one and the same two swapped\n wrote out/alias — and a shell orders a glob however it likes. Inside a folder it was the\n order `readdir` returned, which differs between filesystems, so copying a study to\n another machine could rename its output. A recording that is not a link is preferred over\n a link to it, since that is the name the recording actually has; two of a kind are\n settled by the path that sorts first.\n */\n const identified: { entry: Input; identity: string; link: boolean }[] = [];\n for (const entry of found) {\n const identity = await realpath(entry.path).catch(() => `?${path.resolve(entry.path)}`);\n const own = await lstat(entry.path).catch(() => null);\n identified.push({ entry, identity, link: own?.isSymbolicLink() ?? false });\n }\n\n const byIdentity = new Map<string, Input>();\n const winners = new Map<string, (typeof identified)[number]>();\n for (const candidate of identified) {\n const held = winners.get(candidate.identity);\n if (held !== undefined && !outnames(candidate, held)) continue;\n winners.set(candidate.identity, candidate);\n byIdentity.set(candidate.identity, candidate.entry);\n }\n\n // A directory hands its entries back in whatever order the filesystem stored them.\n const unique = [...byIdentity.values()];\n unique.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n\n /*\n One unreadable path, however many ways it was named — the same rule the recordings above\n get, which this did not.\n\n Each named directory is walked separately and its findings appended, so a folder given\n twice, given relatively and absolutely, or given alongside a link to it, reported every\n path it could not read once per name. `edf2csv study study` on a study holding one locked\n sub-directory printed the error twice and closed with \"Converted 1 of 1 recordings; 2\n paths could not be read\" — one path, counted two — while the recordings beside it were\n correctly converted once. The count is the part that matters: it is what tells someone how\n much of their study was not looked at.\n\n Identity, not spelling, for the reason 0.5.64 gives: `study/locked` and `alias/locked` are\n one directory. `realpathSync` resolves it even when the directory itself cannot be opened,\n since that needs search permission on the parent rather than on the target. A path that\n cannot be resolved at all keeps its own identity, so a name that is simply not there still\n reports itself.\n */\n const seen = new Set<string>();\n const distinct: string[] = [];\n for (const entry of unreadable) {\n let identity: string;\n try {\n identity = realpathSync(entry);\n } catch {\n identity = `?${path.resolve(entry)}`;\n }\n if (seen.has(identity)) continue;\n seen.add(identity);\n // The first spelling is kept, which is the one the caller typed first.\n distinct.push(entry);\n }\n\n return { inputs: unique, unreadable: distinct, namedDirectory };\n}\n\n/**\n * Which of two names for one recording the output should be called after.\n *\n * A name the recording actually has beats a link pointing at it, and two of a kind are\n * settled by sort order. Both are properties of the names, so the answer does not move when\n * a shell expands a glob differently or a filesystem enumerates a folder in another order.\n */\nfunction outnames(\n candidate: { entry: Input; link: boolean },\n held: { entry: Input; link: boolean },\n): boolean {\n if (candidate.link !== held.link) return !candidate.link;\n /*\n Resolved, not as typed. `study/night-01/rec.edf` and `./study/night-01/rec.edf` are the\n same file spelled two ways, and comparing the raw strings made them two different names —\n so this returned here, on lexicographic order of the spelling, and never reached the depth\n rule below that exists for exactly this case.\n\n What that cost is not cosmetic. `edf2csv study study/night-01/rec.edf` converted both\n recordings and exited 0; the identical request written `edf2csv study\n ./study/night-01/rec.edf` was refused with \"would both be converted into \"out/rec\", so one\n would overwrite the other\", exit 2, nothing written. Same files, same folder, same\n intention — and a leading `./` decided whether the run happened. Without the sibling that\n collides it was quieter and no better: one spelling wrote `out/night-01/rec` and the other\n `out/rec`, so a script that started passing absolute paths moved its output.\n\n Through `realpathSync`, not `path.resolve`, because a lexical resolve is not enough: on\n macOS `$TMPDIR` sits under `/var`, which is a link to `/private/var`, so a folder walked\n from the name the caller gave and a recording named relative to the process's own\n directory come out with different absolute prefixes for the same file. Falls back to the\n lexical form for a path that cannot be resolved, which is the same answer as before.\n\n Following links here is safe: a link and its target reach this as two genuinely different\n names, and the rule above has already preferred the real one, so the only pairs left with\n one identity are two spellings or two links — and both should fall through to the depth\n rule below rather than be settled by how they were typed.\n */\n const spelled = (entry: Input): string => {\n try {\n return realpathSync(entry.path);\n } catch {\n return path.resolve(entry.path);\n }\n };\n if (spelled(candidate.entry) !== spelled(held.entry)) {\n return spelled(candidate.entry) < spelled(held.entry);\n }\n /*\n One recording, one path, two names — reached directly and through a named folder.\n\n `edf2csv study study/night-01/rec.edf` and the same two swapped are the same request, and\n they disagreed: the folder gives the recording its position inside the folder\n (`night-01/rec`), a direct mention gives it its bare name (`rec`), and whichever spelling\n the loop met first won. So argument order decided the output directory's name, and, once\n a second `study/rec.edf` was in play, decided whether the run happened at all — the bare\n name collides with it and the run is refused, exit 2, while the other order converts both.\n\n The nested name wins. It is what the folder promised — \"the layout is kept: recordings in\n sub-folders come out in sub-folders\" — and it is the one that does not collide, since\n collapsing a recording to its bare name is what puts it on top of a sibling.\n */\n const depth = (entry: Input): number => entry.name.split(path.sep).length;\n if (depth(candidate.entry) !== depth(held.entry)) return depth(candidate.entry) > depth(held.entry);\n return candidate.entry.name < held.entry.name;\n}\n\n/**\n * Every recording under a directory, following symbolic links.\n *\n * Written by hand rather than with a recursive `readdir` because that reports a symlink as\n * a symlink and never as a file, so a linked recording was skipped without a word — and the\n * closing \"converted 3 of 3\" then described the three it had noticed rather than what was in\n * the folder. Data organised by linking recordings into a working directory is ordinary, and\n * quietly converting fewer files than were asked for is the failure this tool exists to\n * avoid. Naming the same link on the command line always worked, which made the omission\n * harder to notice rather than easier.\n *\n * Following links means they can form a cycle, so directories are recorded by their resolved\n * identity and visited once. A link to a file already reached another way is likewise\n * converted once.\n */\nasync function walk(root: string): Promise<{ files: string[]; unreadable: string[] }> {\n const files: string[] = [];\n const unreadable: string[] = [];\n const seen = new Set<string>();\n /*\n A queue, taken from the front, and each directory's children entered in a settled order.\n\n This was a stack popped from the back, so which of two names for one directory was\n visited first came down to the order `readdir` happened to return them — and the loser\n was then skipped as already seen, taking its name out of the run with it. A folder\n holding `aaa-real/` beside `zzz-alias -> aaa-real` converted into `<out>/zzz-alias/`: the\n link's name, chosen by a hash order that differs between filesystems. 0.4.29 settled this\n for two names of one *file* and left the directory above it deciding by accident.\n\n Breadth first, and each directory's sub-directories entered with real names before links\n and alphabetically within each, so the name that survives is a property of the tree: the\n shallowest, then the one that is not a link, then the first in sort order.\n */\n const queue = [root];\n let next = 0;\n\n while (next < queue.length) {\n const directory = queue[next++] as string;\n const real = await realpath(directory).catch(() => directory);\n if (seen.has(real)) continue;\n seen.add(real);\n\n /*\n A directory that cannot be listed is reported, not stepped over.\n\n Skipping it in silence meant a folder holding three recordings, one of them inside a\n sub-directory without read permission, converted two and said \"Converted 2 of 2\n recordings\" — a total that agreed with itself and with nothing else. That is the same\n failure 0.4.4 fixed for symbolic links, arriving by a different route: converting fewer\n recordings than were asked for and reporting success.\n */\n let entries;\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch {\n unreadable.push(directory);\n continue;\n }\n\n // A real name before a link to the same thing, then alphabetically. The dirent describes\n // the entry itself rather than its target, which is exactly the question here.\n const ordered = [...entries].sort((a, b) => {\n const link = Number(a.isSymbolicLink()) - Number(b.isSymbolicLink());\n return link !== 0 ? link : a.name < b.name ? -1 : a.name > b.name ? 1 : 0;\n });\n\n for (const entry of ordered) {\n const full = path.join(directory, entry.name);\n // stat, not the dirent: a dirent describes the link, and what matters is its target.\n const info = await stat(full).catch(() => null);\n if (info === null) {\n /*\n Anything that cannot be inspected is reported, whatever it is called.\n\n This used to report only names ending in `.edf` or `.bdf`, on the reasoning that a\n broken link to something else is nobody's business. A directory carries no such\n name. A study kept as one folder per night, with one night linked to an external\n drive, converted the nights that were mounted and said nothing about the one that\n was not — and because losing that input left a single recording, `--out` stopped\n meaning \"a parent to fill\" and started meaning \"the directory to write\", so the\n survivor landed somewhere else as well. Whether a drive happened to be mounted\n changed both what was converted and where it went, in silence, exit 0.\n\n The walk cannot know what was behind a link it cannot follow, which is exactly the\n reason to say so rather than to guess.\n */\n unreadable.push(full);\n continue;\n }\n if (info.isDirectory()) {\n queue.push(full);\n } else if (info.isFile() && /\\.(edf|bdf)$/iu.test(entry.name)) {\n // Duplicates are collapsed once for the whole list in expandInputs; this only has\n // to find them. The `seen` set here is for directory cycles.\n files.push(full);\n }\n }\n }\n return { files, unreadable };\n}\n\n/**\n * Where each recording's output goes.\n *\n * Naming one recording, `--out` is the output directory itself, which is what it has always\n * meant. Naming a folder, or several recordings, it is a parent and each recording gets its\n * own directory inside it, because writing several recordings into one directory would have\n * them overwrite each other's `signals.csv` — the one thing a batch must not do quietly.\n *\n * With no `--out` at all, every recording converts beside itself exactly as it would have\n * done alone, so a glob behaves like the shell loop it replaces.\n *\n * What a folder means is decided by the folder, not by how much is in it. Counting the\n * recordings instead meant `edf2csv study --out csv` wrote `csv/signals.csv` while the study\n * held one night and `csv/night-01/rec/signals.csv` once it held two: adding a recording\n * moved the output of a recording that had not changed. The same count made the destination\n * depend on things no one had touched either — a night on an unmounted drive, a\n * sub-directory that could not be read — so where the data landed turned on the state of the\n * machine rather than on the command.\n */\nfunction destinationsFor(\n inputs: readonly Input[],\n out: string | undefined,\n /** True when a folder was named, or more than one recording was. See `batch` in `main`. */\n batch: boolean,\n): string[] {\n if (out === undefined) return inputs.map((input) => defaultOutputDir(input.path));\n if (!batch) return [out];\n return inputs.map((input) => path.join(out, stemOf(input.name)));\n}\n\n/** A name without its extension, keeping a leading dot: `.hidden.edf` -> `.hidden`. */\nfunction stemOf(name: string): string {\n const dot = name.lastIndexOf('.');\n const slash = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\\\'));\n return dot > slash + 1 ? name.slice(0, dot) : name;\n}\n\n/**\n * Refuse a batch in which two recordings would land in the same directory.\n *\n * Two files with the same name in different folders — `night-1/rec.edf` and\n * `night-2/rec.edf`, which is how recordings usually get organised — both resolve to\n * `<out>/rec`. Converting them in turn would leave one recording's data sitting under the\n * other's name with nothing to show it had happened, so the run stops before writing\n * anything at all.\n */\nfunction assertDistinct(inputs: readonly string[], destinations: readonly string[]): void {\n /*\n Two names that differ only in case are one directory on a filesystem that does not\n distinguish them, which is the default on macOS and the rule on Windows.\n\n `a/REC.edf` and `b/rec.edf` produce `<out>/REC` and `<out>/rec`. Compared exactly those\n are different, so both went through, and with --force the second conversion wrote into\n the directory the first had made: one directory holding one recording's `signals.csv`\n beside the other's `signals_256hz.csv`, under a single `metadata.json` naming only one\n of them. The run reported \"Converted 2 of 2 recordings\" and exited 0. A directory whose\n provenance file describes a recording other than the data beside it is the one outcome\n this tool exists to prevent.\n\n The comparison follows the platform rather than being applied everywhere, so a\n case-sensitive filesystem — where those really are two directories — keeps converting\n both. A case-sensitive volume on macOS is the exception it gets wrong, and it gets it\n wrong in the safe direction: a refusal that names both recordings, not a silent merge.\n */\n const foldsCase = process.platform === 'darwin' || process.platform === 'win32';\n /*\n macOS filesystems fold Unicode normalisation as well as case.\n\n HFS+ and APFS compare names in a normalised form, so `café` written as e + U+0301 and\n `café` written as U+00E9 are one directory — while remaining two different JavaScript\n strings, which is all this guard was comparing. Two recordings whose stems differ only\n that way therefore both passed the check and both converted into the same place:\n\n study/café.edf (NFC) -> csv/café/signals.csv\n study/café.bdf (NFD) -> csv/café/signals_256hz.csv, _128hz, _1hz\n\n one directory holding both, under a single metadata.json naming one of them, reported as\n \"Converted 2 of 2 recordings\" and exit 0 under --force. Without --force the second run\n happened to hit \"already exists\", which is the accidental save rather than the check\n doing its job — and it named the wrong problem.\n\n Not folded on Linux, where the two names are genuinely two directories and refusing them\n would be inventing a collision that does not exist. Windows preserves normalisation too,\n so only darwin. This is the same platform-shaped assumption the case fold above already\n makes, and it has the same limit: a network or removable volume that normalises while the\n running platform does not is not covered by either.\n */\n const identity = (destination: string): string => {\n const resolved = path.resolve(destination);\n if (!foldsCase) return resolved;\n const folded = resolved.toLowerCase();\n return process.platform === 'darwin' ? folded.normalize('NFC') : folded;\n };\n\n const claimed = new Map<string, string>();\n for (const [index, destination] of destinations.entries()) {\n const key = identity(destination);\n const first = claimed.get(key);\n if (first !== undefined) {\n throw new OptionError(\n `\"${printable(inputs[index] as string)}\" and \"${printable(first)}\" would both be converted into \"${printable(destination)}\", ` +\n `so one would overwrite the other.\\n` +\n `Convert them separately, or rename one of them.`,\n );\n }\n claimed.set(key, inputs[index] as string);\n }\n\n /*\n And refuse one destination sitting inside another.\n\n A recording named `rec.edf` beside a folder named `rec` is enough: their outputs are\n `<out>/rec` and `<out>/rec/inner`, which are not equal, so the check above let both\n through. What happened next depended on which conversion got there first. Each creates\n its own directory with a single non-recursive mkdir but its parents recursively, so\n whichever started second either claimed a directory the other had already made as a\n parent — failing with \"already exists\" — or did not. Five runs in twenty failed that way\n under --jobs 2, converting one recording of two; the same command succeeded the other\n fifteen times.\n\n Sorting first puts an ancestor next to its descendant: anything sorting between them\n shares the same prefix, and would be caught as its own adjacent pair.\n */\n /*\n Each destination is checked against its own ancestors, by name.\n\n The first version of this sorted the resolved paths and compared neighbours, on the\n reasoning that an ancestor and its descendant end up adjacent. They do not: the\n separator is not the lowest character, so any sibling whose name begins with one of the\n thirteen printable characters below '/' lands between them. With `rec.edf`, `rec!x.edf`\n and `rec/inner.edf` in one folder, '!' sorts between `out/rec` and `out/rec/inner` and\n the pair was never compared — three recordings converted, one of them inside another's\n directory, reported as \"Converted 3 of 3\".\n\n Walking up from each destination has no such gap. Output trees are shallow, so this is a\n handful of string lookups per recording.\n */\n const byDestination = new Map<string, number>();\n for (const [index, destination] of destinations.entries()) {\n byDestination.set(identity(destination), index);\n }\n\n for (const [index, destination] of destinations.entries()) {\n let ancestor = path.dirname(path.resolve(destination));\n for (let parent = ''; ancestor !== parent; ancestor = path.dirname(ancestor)) {\n parent = ancestor;\n const owner = byDestination.get(identity(ancestor));\n if (owner === undefined || owner === index) continue;\n /*\n All four paths escaped, not the two nearest the front of the sentence.\n\n A file name may hold a newline on every platform this runs on, and `printableLines`\n splits a message on newlines to indent its continuations — so the two raw paths broke\n the sentence into three lines and printed half a path on each:\n\n error: \"study/re\\x0ac/inner.edf\" would be converted into \"o/re\\x0ac/inner\",\n which is inside \"o/re\n c\" — where \"study/re\n c.edf\" is converted.\n\n Two of the four names read as one thing and two as two, in the same sentence, about\n the same pair of paths. That is 0.5.67's defect — a name holding a newline splitting\n `Wrote` across two lines — in the one message that names four paths at once.\n */\n throw new OptionError(\n `\"${printable(inputs[index] as string)}\" would be converted into \"${printable(destination)}\", which is inside ` +\n `\"${printable(destinations[owner] as string)}\" — where \"${printable(inputs[owner] as string)}\" is converted.\\n` +\n `One recording's output cannot sit inside another's. Convert them separately, or ` +\n `rename one of them.`,\n );\n }\n }\n}\n\n/**\n * The exit code for a run in which several files failed for different reasons.\n *\n * 2 means \"you invoked it wrong\" and 1 means \"something went wrong with a file\". When both\n * happened, 1 is the honest answer: the invocation cannot be the whole story once a file has\n * genuinely failed. With a single input there is only ever one code, so its exit status is\n * exactly what it always was.\n */\nfunction worstOf(codes: readonly number[]): number {\n /*\n Anything that is not \"the command line is the problem\" is a failure of the run.\n\n This tested for `EXIT_ERROR` and fell through to `EXIT_USAGE` for everything else, on the\n assumption that a child exits 1 or 2. A child killed by a signal exits 130 or 143 — its\n own interrupt handler does that — so a `--jobs` worker stopped by SIGTERM made the batch\n exit 2, which cli-reference's table defines as \"The command line is the problem\" and\n warnings-and-errors as \"The command was invoked incorrectly, or asked for something the\n recording can't provide\". The command was fine; something killed a worker. The\n out-of-memory killer and a scheduler's time limit both arrive exactly this way, and the\n serial path reports the same event as the failure it is.\n\n Asking the question the other way round is also the honest one: 2 is the narrow claim,\n so it should be the one that has to be earned by every code in the list.\n */\n return codes.some((code) => code !== EXIT_USAGE) ? EXIT_ERROR : EXIT_USAGE;\n}\n\n/**\n * Convert one recording in a separate process, and collect everything it printed.\n *\n * The arguments are rebuilt from the parsed options rather than sliced out of argv, so the\n * child receives exactly the flags this run was given and nothing that only makes sense to\n * the parent: not the other recordings, not --jobs, and not --info or --stdout, neither of\n * which reaches this path.\n */\nasync function convertInChild(\n input: string,\n destination: string,\n values: Record<string, unknown>,\n running: Map<ChildProcess, string>,\n): Promise<{\n code: number;\n out: string;\n err: string;\n report: { converted: number; warnings: number } | null;\n}> {\n /*\n The recording goes last, behind `--`.\n\n As the first argument it was parsed as an option whenever its path began with a dash,\n which `path.join` produces from a folder given as `.` — `./-lead.edf` normalises to\n `-lead.edf`. The child then failed on a file the parent had converted happily, so the\n same command converted two recordings serially and one under --jobs.\n */\n /*\n Option values go in the `--flag=value` form, never as two arguments.\n\n Split across two arguments, a value beginning with a dash is another option as far as the\n child's parser is concerned: `--out ./-nightly` reached it as `--out` followed by\n `-nightly`, and the child died on \"Option '--out' argument is ambiguous\" while the serial\n path converted the same command without complaint. A leading dash is not exotic —\n `path.join` produces one from a folder given as `.`, and directories get named after\n dates and flags often enough. Same failure 0.4.19 fixed for the recording's own path, on\n everything that carries a value rather than on the positional.\n */\n const args = [`--out=${destination}`];\n /*\n --strict is deliberately absent: it is a verdict on the whole run, and a child converting\n one recording is not the whole run. Passing it down made each child announce \"--strict: 1\n warning raised, so this run is reported as a failure\" about its own single file, and exit\n 1 for it, which the parent then counted as a conversion that had not happened. The parent\n applies it once, from the counts the children report.\n */\n for (const flag of ['annotations-only', 'checksum', 'gzip', 'bom', 'force', 'quiet', 'json']) {\n if (values[flag] === true) args.push(`--${flag}`);\n }\n for (const flag of ['start', 'duration', 'end', 'decimals', 'layout']) {\n if (typeof values[flag] === 'string') args.push(`--${flag}=${values[flag] as string}`);\n }\n // --channels is repeatable, and each term is passed as given so that a label containing a\n // comma survives: joining them back into one list would split it in the child.\n const channels = values['channels'];\n if (channels !== undefined) {\n for (const term of Array.isArray(channels) ? (channels as string[]) : [String(channels)]) {\n args.push(`--channels=${term}`);\n }\n }\n args.push('--', input);\n\n return new Promise((resolve) => {\n const child = fork(fileURLToPath(import.meta.url), args, {\n stdio: ['ignore', 'pipe', 'pipe', 'ipc'],\n });\n running.set(child, destination);\n let out = '';\n let err = '';\n let report: { converted: number; warnings: number } | null = null;\n child.on('message', (message: unknown) => {\n const payload = (message as { edf2csv?: { converted: number; warnings: number } })?.edf2csv;\n if (payload) report = payload;\n });\n child.stdout?.setEncoding('utf8').on('data', (chunk: string) => {\n out += chunk;\n });\n child.stderr?.setEncoding('utf8').on('data', (chunk: string) => {\n err += chunk;\n });\n child.on('error', (error) => {\n running.delete(child);\n /*\n Escaped, which every other place this file prints a recording's name has been since\n 0.5.67 and this one was not.\n\n A file name is untrusted text — the filesystem supplies it and a recording inside a\n scanned folder may be called anything, including a name carrying an ESC byte — and\n `named`, `reportError`, the `[n/m]` header and the interrupt handler all put it\n through `printable`. This line is the one that fires when the fork itself fails, which\n is the moment a batch of five hundred at `--jobs auto` runs out of file descriptors,\n and it is the only line in this file that prints a name straight to stderr.\n */\n resolve({\n code: EXIT_ERROR,\n out,\n err: `${err}error: ${printable(input)}: ${printableLines(error.message, ' ')}\\n`,\n report,\n });\n });\n child.on('close', (code, signal) => {\n running.delete(child);\n /*\n A child that was killed says nothing on its way out, so the parent has to.\n\n `code` is null when a process dies by signal, which left the parent with `code ??\n EXIT_ERROR` — a failure with an empty `err`, so the run printed nothing but \"Converted\n 1 of 2 recordings; 1 failed.\" and stopped. Nothing said which recording, nothing said\n why, and nothing said that its directory held a 194 MB signals.csv cut off mid-row\n with no channels.csv beside it. That file looks exactly like a finished one to\n anything that opens it.\n\n The out-of-memory killer, a job scheduler's time limit and `kill` all arrive this way.\n A run stopped from the keyboard is reported by the interrupt handler instead, which\n names every directory at once rather than one line per child.\n */\n if (signal !== null && !stopping) {\n // And the same question of this one child, for the same reason as the handler above.\n err +=\n `error: stopped by ${signal} before it finished.\\n` +\n detail(\n existsSync(destination)\n ? `Incomplete, and should not be used: ${printable(destination)}`\n : `Nothing was written: \"${printable(destination)}\" was never created.`,\n );\n }\n resolve({ code: code ?? EXIT_ERROR, out, err, report });\n });\n });\n}\n\n/**\n * Put the recording's name into the lines a child produced.\n *\n * Errors always: a failure has to say which recording failed, whatever else is on screen.\n * Warnings only when `alsoWarnings` — under --quiet, where the `[n/m]` header that would\n * otherwise carry the attribution is not printed. See the call site.\n */\nfunction named(text: string, input: string, alsoWarnings = false): string {\n // A function, not a string: `$&`, `$\\'`, `` $` `` and `$1` in a replacement string are\n // patterns, and a file may legitimately be called any of them. `bad$&name.edf` re-injected\n // the text it had just matched and reported itself as `baderror: name.edf`.\n const heads = alsoWarnings ? /^(error|warning|note): /gmu : /^error: /gmu;\n return text.replace(heads, (head) => `${head}${printable(input)}: `);\n}\n\n/** Re-render a child's pretty-printed summary onto one line, leaving anything else alone. */\nfunction compactJson(text: string): string {\n const trimmed = text.trim();\n if (trimmed === '') return '';\n try {\n return `${JSON.stringify(JSON.parse(trimmed))}\\n`;\n } catch {\n // Not the document expected; passing it through unchanged beats losing it.\n return text;\n }\n}\n\n/**\n * Where a conversion's output goes.\n *\n * Serial runs write straight through, which is what they have always done. Running several\n * conversions at once needs the alternative: each one's lines are collected and released in\n * a block when it finishes, so two recordings finishing together cannot interleave a summary\n * with a warning belonging to the other file.\n */\ntype Emit = (stream: 'out' | 'err', text: string) => void;\n\nconst writeThrough: Emit = (stream, text) => {\n (stream === 'out' ? process.stdout : process.stderr).write(text);\n};\n\n/** Collects output so it can be released in one piece when a conversion finishes. */\nfunction buffered(): { emit: Emit; flush: () => void } {\n const parts: [('out' | 'err'), string][] = [];\n return {\n emit: (stream, text) => parts.push([stream, text]),\n flush: () => {\n for (const [stream, text] of parts) writeThrough(stream, text);\n },\n };\n}\n\n/**\n * Report an error the way this command always has, and give the exit code it implies.\n *\n * `input` names the recording while a batch is running: several failures otherwise arrive\n * as a stack of messages with nothing saying which file each belongs to.\n */\nfunction reportError(error: unknown, input?: string, emit: Emit = writeThrough): number {\n const where = input === undefined ? '' : `${printable(input)}: `;\n if (error instanceof EdfError || error instanceof ConversionError) {\n emit('err', `error: ${where}${printableLines(error.message, ' ')}\\n`);\n /*\n Wrapped, for the same reason the hint under a warning is — and because on this file\n it is frequently the same sentence.\n\n A mixed-rate recording refused by --stdout and the same recording described by --info\n both end in \"Narrow it to one rate with --channels, write --layout long ...\". One\n arrived as a ConversionError hint and printed on a single 130-column line; the other\n arrived as a Diagnostic hint and wrapped at 80 from 0.7.1. Identical advice about an\n identical file, laid out two different ways depending on whether the tool went on to\n convert it.\n */\n /*\n Escaped, which the message above it has been since 0.5.67 and this line never was.\n\n A hint is usually a fixed sentence, which is why it looked like text nobody supplies.\n One is not: a read that fails part way through a conversion reports \"What was written\n to <outputDir> before it failed is incomplete\", and `--out` takes whatever path the\n caller gives it. A directory may be named with an ESC byte on every platform this\n runs on, so `--out $'out\\x1b[31mred'` on a recording that shrinks mid-read put a live\n colour change on stderr — under an `error:` line that had been escaped correctly, two\n lines above.\n */\n if (error.hint) emit('err', `${wrap(printable(error.hint), ' ')}\\n`);\n // A request the tool cannot carry out is the command line's problem, not the file's,\n // whatever layer noticed it. See USAGE_ERROR_CODES.\n return error instanceof ConversionError && USAGE_ERROR_CODES.has(error.code)\n ? EXIT_USAGE\n : EXIT_ERROR;\n }\n if (\n error instanceof ChannelSelectionError ||\n error instanceof TimeRangeError ||\n error instanceof OptionError\n ) {\n emit('err', `error: ${where}${printableLines(error.message, ' ')}\\n`);\n return EXIT_USAGE;\n }\n emit('err', `error: ${where}${message(error, ' ')}\\n`);\n return EXIT_ERROR;\n}\n\n/**\n * How many conversions to run at once.\n *\n * `auto` is one per core, less one, so a long batch does not take the machine over. A batch\n * is the only place this means anything: a single recording is a single conversion however\n * many jobs are asked for.\n */\nfunction parseJobs(raw: unknown, inputs: number): number {\n if (raw === undefined) return 1;\n const text = String(raw).trim();\n /*\n The cores this process may use, not the ones the machine has.\n\n `cpus().length` counts every core the kernel can see, which is the machine's answer to a\n question about this process. A container given two CPUs of a sixty-four core node, a job\n pinned by `taskset` or a scheduler, a `docker --cpuset-cpus` — in all of them `auto` asked\n for sixty-three workers, which is exactly the \"leaves the machine usable\" this setting\n exists for, inverted. `availableParallelism` is the call for the question and has been\n there since Node 18.14; this package requires 20.\n */\n if (text === 'auto') return Math.max(1, Math.min(inputs, availableParallelism() - 1));\n /*\n A whole number as written, not as `Number()` chooses to read it.\n\n `Number.isInteger(Number(text))` accepted `0x10`, `1e3` and `999999999999999999999` — the\n last of which is not an integer any more, only the nearest double to one — while refusing\n `4.7`. The message says \"a whole number of 1 or more\" and the refusal of 4.7 is what makes\n a reader believe it, so the others read as the tool silently reinterpreting what they\n typed. `--jobs` is the flag 0.4.2 hardened because accepting `--jobs 0` in silence \"is the\n kind of quiet this tool avoids\".\n\n Surrounding space is trimmed above and is not one of those: ` 4 ` is four to anyone who\n reads it, and a value assembled by a shell out of a file or a `$(...)` routinely carries\n some. What matters is that the refusal quote what was typed rather than what survived the\n trim — see below.\n */\n if (!/^\\d+$/u.test(text)) {\n throw new OptionError(`--jobs must be a whole number of 1 or more, or \"auto\", got \"${quotedValue(raw)}\".`);\n }\n const value = Number(text);\n if (!Number.isSafeInteger(value) || value < 1) {\n throw new OptionError(`--jobs must be a whole number of 1 or more, or \"auto\", got \"${quotedValue(raw)}\".`);\n }\n return Math.min(value, Math.max(1, inputs));\n}\n\nfunction splitChannels(raw: unknown): string[] | undefined {\n if (raw === undefined) return undefined;\n const list = Array.isArray(raw) ? (raw as string[]) : [String(raw)];\n const terms = list.flatMap((entry) => entry.split(',')).map((t) => t.trim()).filter((t) => t !== '');\n // Returning undefined here would mean \"no --channels given\" and convert everything,\n // which is the opposite of what someone passing an empty list is asking for.\n if (terms.length === 0) {\n throw new OptionError('--channels was given but lists no channel names.');\n }\n return terms;\n}\n\n/**\n * `--start`, `--duration` and `--end`, in seconds.\n *\n * The first two name a position on the recording's own clock, which a file timed from before\n * zero puts below it, so those take a sign. `--duration` is a length and does not.\n */\nfunction optionalTime(raw: unknown, option: string): number | undefined {\n if (raw === undefined) return undefined;\n return parseTimeSpec(String(raw), option, option !== '--duration');\n}\n\n/** `--layout`, which is one of two words and not a guess at what was meant. */\nfunction optionalLayout(raw: unknown): 'wide' | 'long' | undefined {\n if (raw === undefined) return undefined;\n // Trimmed, like every other option's value. This one alone refused ` long`, so a value that\n // came out of a file or a `$(...)` with a newline on it was rejected for a character nobody\n // typed and which the message could not show.\n const text = String(raw).trim();\n if (text === 'wide' || text === 'long') return text;\n throw new OptionError(`--layout must be \"wide\" or \"long\", got \"${quotedValue(raw)}\".`);\n}\n\n/**\n * An option's value as the user typed it, for a message that quotes it back.\n *\n * The point of the quotation marks is to show where the value begins and ends, which matters\n * most for the values that went wrong because of what surrounds them. `--jobs` trimmed first\n * and then quoted the remains, so `--jobs \" x\"` came back as `got \"x\"` and `--jobs \" \"` as\n * `got \"\"` — the second reading as though nothing had been given at all, when what was given\n * is the whole reason it failed. `--start` has quoted the typed value since 0.5.60 and\n * `--decimals` since 0.5.86; this is the same rule, spelled once.\n */\nfunction quotedValue(raw: unknown): string {\n return String(raw);\n}\n\nfunction optionalDecimals(raw: unknown): number | undefined {\n if (raw === undefined) return undefined;\n const text = String(raw).trim();\n // Number('') is 0, which would quietly round every physical value to a whole number.\n if (text === '') {\n throw new OptionError('--decimals needs a number, for example --decimals 3.');\n }\n /*\n A whole number as written, not as `Number()` chooses to read it.\n\n `Number.isInteger(Number(text))` accepted `0x3`, `0b11`, `0o5`, `3e0` and `+3`,\n while refusing `3.5` — and the refusal of `3.5` is what makes a reader believe the message\n that says \"a whole number between 0 and 20\". So `--decimals 0o5` wrote five decimals for\n an argument that reads as five to nobody, and `--decimals 0b11` wrote three, in silence,\n exit 0, with a CSV that looks exactly as intended.\n\n The same fix `--jobs` got, and the same one `--channels '#N'` got — where the comment\n records why: \"Every one of them selected a channel and exited 0, so a slip did not fail —\n it quietly converted a different channel than the one asked for, which for this tool is\n the worst way to be wrong.\" A precision is the same kind of quiet.\n */\n const value = Number(text);\n if (!/^\\d+$/u.test(text) || !Number.isInteger(value) || value < 0 || value > 20) {\n throw new OptionError(`--decimals must be a whole number between 0 and 20, got \"${String(raw)}\".`);\n }\n return value;\n}\n\n/*\n Error text is escaped for the same reason --info's table is: a fatal header error quotes\n the channel label that caused it, and that label is free text out of the file. A recording\n declaring a negative sample count under a label containing `\\x1b[2J` cleared the reader's\n screen on the way out.\n*/\n/**\n * A `parseArgs` failure, in this tool's words where they are better than Node's.\n *\n * Node refuses `--out -nightly` with \"Option '--out' argument is ambiguous. Did you forget to\n * specify the option argument for '--out'? To specify an option argument starting with a dash\n * use '--out=-XYZ'.\" The user did not forget anything — they gave a value — and `-XYZ` is a\n * placeholder, where every other message this tool prints quotes what was actually typed and\n * says exactly what to type instead. A destination whose name starts with a dash is not exotic,\n * and neither is a negative `--start` on a recording timed from before zero.\n *\n * 0.4.34 already fixed this message where the tool produced it internally, building child argv\n * for `--jobs` — `--out ./-nightly` reached the child as two arguments and died on it, while the\n * serial path converted the same command. The half a user can hit was left as Node wrote it.\n *\n * An unknown option is reworded too, and for a plainer reason than tone. Node's sentence is\n * \"Unknown option '--chanels'. To specify a positional argument starting with a '-', place it at\n * the end of the command after '--', as in '-- \"--chanels\"' — except the closing quote is not\n * there. Node opens one before `--` and never closes it, so this tool printed an unfinished\n * sentence, without the `error:` prefix every other refusal carries, at the single most common way\n * to get a command wrong. The advice is also for a different mistake than the one almost everyone\n * makes: it explains how to pass a *file* whose name begins with a dash, where the reader has\n * mistyped one of twenty flags.\n *\n * Node's remaining two — a switch given a value, an option missing its value — say something true\n * in words a reader can act on, and replacing them with near-identical sentences would be churn.\n * They do go out with the prefix, which is the other half of what was wrong with the unknown-option\n * message above and was left behind when its wording was fixed:\n *\n * $ edf2csv recording.edf --decimals\n * Option '--decimals <value>' argument missing\n * $ edf2csv recording.edf --gzip=yes\n * Option '--gzip' does not take an argument\n *\n * against `error:` on every other refusal this tool prints, including the one for `--decimals 101`\n * one keystroke away. This repository's own stdout audit picks errors out of stderr with\n * `line.startsWith('error:')`, so by its own reckoning these two runs printed no error and exited 2.\n */\n/**\n * A token as it has to be typed for a shell to hand it over unchanged.\n *\n * Every hint in this file that prints a command has to print one that works — the comment on\n * the `--channels` advice in the header parser lists three separate times it did not. These\n * two are the same failure at the command line rather than in a channel label. `--out \"-my\n * nightly\"` was answered with `Write it as one argument instead: --out=-my nightly`, which is\n * two arguments; `--chan\"nels` was answered with `edf2csv -- \"--chan\"nels\"`, whose quotes\n * collapse into something else again.\n *\n * Left bare when a shell would read it as written, so the ordinary `--out=-nightly` reads as\n * it always has. Otherwise single-quoted, the one POSIX form with no escapes inside it: a\n * single quote in the token closes, escapes and reopens.\n */\nfunction survivesBare(text: string): boolean {\n return text !== '' && !/[^\\w@%+=:,./-]/u.test(text);\n}\n\nfunction singleQuoted(text: string): string {\n return `'${text.replaceAll(\"'\", \"'\\\\''\")}'`;\n}\n\nfunction usageMessage(error: unknown, argv: readonly string[]): string {\n /** Node's own wording, carrying this tool's prefix. */\n const asError = (text: string): string => (text.startsWith('error:') ? text : `error: ${text}`);\n const raw = message(error);\n const code = (error as { code?: unknown } | null)?.code;\n if (code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') return unknownOption(raw);\n if (code !== 'ERR_PARSE_ARGS_INVALID_OPTION_VALUE') {\n return asError(raw);\n }\n const ambiguous = /^Option '(-[^']+)' argument is ambiguous/u.exec(raw);\n if (!ambiguous) return asError(raw);\n\n const flag = ambiguous[1] as string;\n /*\n The value is whatever followed the flag. Read from argv rather than from the message,\n which does not carry it — and if it is not there after all, Node's text is still true.\n\n The occurrence Node stopped at, which is the first one whose value begins with a dash and\n not simply the first occurrence of the flag. A flag given twice put the wrong value in the\n sentence: `--end 1e3 --end -5` was refused with `--end was given \"1e3\", which begins with\n a dash`, over a value that does not, advising `--end=1e3` — a rewrite of the half that was\n already fine, leaving the half that was not exactly as it was. `--channels` is repeatable,\n so two of them is an ordinary command rather than a mistake, and that is where it reads\n worst: the message quotes a channel name and calls it a flag.\n */\n let at = -1;\n for (let i = 0; i < argv.length; i++) {\n if (argv[i] !== flag) continue;\n const next = argv[i + 1];\n if (next !== undefined && next.startsWith('-')) {\n at = i;\n break;\n }\n }\n const value = at >= 0 ? argv[at + 1] : undefined;\n if (value === undefined) return asError(raw);\n\n /*\n How the two shapes join, which is not the same and matters.\n\n A long option takes `--out=-nightly`. A short one does NOT take `-o=-nightly`: parseArgs\n reads the equals sign as the first character of the value and hands back \"=-nightly\", so\n that advice would have produced a directory named `=-nightly` without a word — a worse\n failure than the message it replaced, and a silent one. Short options join directly.\n */\n const joined = flag.startsWith('--') ? `${flag}=${value}` : `${flag}${value}`;\n const typeable = survivesBare(joined) ? joined : singleQuoted(joined);\n return printableLines(\n `error: ${flag} was given \"${value}\", which begins with a dash and so reads as another ` +\n `flag rather than as its value.\\n` +\n ` Write it as one argument instead: ${typeable}`,\n );\n}\n\n/** Levenshtein distance, two rows at a time. Twenty names of under twenty characters. */\nfunction editDistance(a: string, b: string): number {\n const row = Array.from({ length: b.length + 1 }, (_unused, i) => i);\n for (let i = 1; i <= a.length; i++) {\n let diagonal = row[0] as number;\n row[0] = i;\n for (let j = 1; j <= b.length; j++) {\n const above = row[j] as number;\n row[j] = Math.min(above + 1, (row[j - 1] as number) + 1, diagonal + (a[i - 1] === b[j - 1] ? 0 : 1));\n diagonal = above;\n }\n }\n return row[b.length] as number;\n}\n\n/**\n * The option they probably meant, or null when nothing is close enough to say.\n *\n * Long options only. A stray `-Z` carries one character of evidence, and guessing a word\n * from it would be guessing.\n *\n * Two rules, because typos come in two shapes. An unfinished name is a prefix — `--chan`,\n * `--decim` — and is only taken from three characters up, so `--c` does not become\n * `--channels` or `--checksum` by coin toss. A misspelt one is within a couple of edits,\n * scaled to the name's length so `--jobs` is not reached from three characters away.\n */\nfunction nearestOption(flag: string): string | null {\n if (!flag.startsWith('--')) return null;\n const typed = flag.slice(2).toLowerCase();\n if (typed.length < 2) return null;\n\n const names = Object.keys(OPTIONS);\n if (typed.length >= 3) {\n const prefixed = names.filter((name) => name.startsWith(typed));\n if (prefixed.length === 1) return `--${prefixed[0] as string}`;\n }\n\n let best: string | null = null;\n let score = Infinity;\n let tied = false;\n for (const name of names) {\n const d = editDistance(typed, name);\n if (d < score) {\n score = d;\n best = name;\n tied = false;\n } else if (d === score) {\n tied = true;\n }\n }\n if (best === null || tied) return null;\n return score <= Math.max(1, Math.min(3, Math.floor(best.length / 3))) ? `--${best}` : null;\n}\n\n/** An option this tool does not have, in a finished sentence, naming the likely one. */\nfunction unknownOption(raw: string): string {\n const found = /^Unknown option '([^']+)'/u.exec(raw);\n // If Node ever rewords it, its text is still true; only the polish is lost.\n if (!found) return raw;\n const flag = found[1] as string;\n const near = nearestOption(flag);\n return printableLines(\n `error: There is no ${flag} option.${near === null ? '' : ` Did you mean ${near}?`}\\n` +\n ` If it is the name of a file, pass it after -- instead:\\n` +\n ` edf2csv -- ${survivesBare(flag) ? `\"${flag}\"` : singleQuoted(flag)}`,\n );\n}\n\nfunction message(error: unknown, indent = ''): string {\n // The indent lines continuation up under an \"error: \" prefix. Usage problems print without\n // one, alongside the usage text, so they pass nothing and stay flush left.\n return printableLines(error instanceof Error ? error.message : String(error), indent);\n}\n\n// `worstOf` is exported for the test that pins the exit-code mapping. A batch's verdict is\n// one of the few things a script branches on, and the mapping is easier to check directly\n// than by racing a signal at a worker.\n// `parseJobs` joins them for the same reason: what `auto` resolves to is a number nothing\n// could see from outside, since the only observable effect is how many children run at once.\nexport { defaultOutputDir, parseJobs, worstOf };\n\n/**\n * Whether this file was executed rather than imported.\n *\n * npm installs a bin as a symlink (node_modules/.bin/edf2csv -> ../edf2csv/dist/cli.js),\n * and that is the path `npx` runs. In that case process.argv[1] is the symlink while\n * import.meta.url is already resolved to the real file, so comparing the two directly\n * reports \"imported\" and the command silently does nothing. Both sides are resolved\n * through realpath before comparing.\n */\nfunction isMainModule(): boolean {\n const entry = process.argv[1];\n if (entry === undefined) return false;\n try {\n return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));\n } catch {\n // An unreadable or deleted entry path is not a reason to refuse to run.\n return pathToFileURL(entry).href === import.meta.url;\n }\n}\n\nconst invokedDirectly = isMainModule();\n\nif (invokedDirectly) {\n main(process.argv.slice(2))\n .then((code) => {\n /*\n A write failure already reported is not erased by a run that thought it finished.\n\n The stdout listener above prints `Writing to stdout failed: ...` and sets EXIT_ERROR,\n and assigning `code` over it put that back to 0 — so `--info > desc.txt` onto a full\n filesystem printed the error, left a zero-byte file, and exited 0. It only looked\n right because the stdout audit threw a second error on the way out, whose message was\n about a short write that had not happened.\n\n A closed pipe deliberately sets no code (see ignoreBrokenPipe), so `--info | head`\n still exits 0 through here.\n */\n process.exitCode = code === EXIT_OK ? (process.exitCode ?? EXIT_OK) : code;\n })\n .catch((error: unknown) => {\n process.stderr.write(`error: ${message(error)}\\n`);\n process.exitCode = EXIT_ERROR;\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edf2csv",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.177",
|
|
4
4
|
"description": "Convert EDF, EDF+ and BDF biosignal recordings (European Data Format) to CSV from the command line. Local, streaming, and never resamples or alters units.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"edf",
|