@cosyte/cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +326 -0
- package/LICENSE +21 -0
- package/README.md +265 -0
- package/dist/bin/cosyte-mcp.cjs +1160 -0
- package/dist/bin/cosyte-mcp.cjs.map +1 -0
- package/dist/bin/cosyte-mcp.d.cts +1 -0
- package/dist/bin/cosyte-mcp.d.ts +1 -0
- package/dist/bin/cosyte-mcp.mjs +1158 -0
- package/dist/bin/cosyte-mcp.mjs.map +1 -0
- package/dist/bin/cosyte.cjs +1785 -0
- package/dist/bin/cosyte.cjs.map +1 -0
- package/dist/bin/cosyte.d.cts +1 -0
- package/dist/bin/cosyte.d.ts +1 -0
- package/dist/bin/cosyte.mjs +1783 -0
- package/dist/bin/cosyte.mjs.map +1 -0
- package/dist/index.cjs +1462 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1360 -0
- package/dist/index.d.ts +1360 -0
- package/dist/index.mjs +1414 -0
- package/dist/index.mjs.map +1 -0
- package/dist/io-B2EobRzC.d.cts +57 -0
- package/dist/io-B2EobRzC.d.ts +57 -0
- package/dist/mcp.cjs +1161 -0
- package/dist/mcp.cjs.map +1 -0
- package/dist/mcp.d.cts +143 -0
- package/dist/mcp.d.ts +143 -0
- package/dist/mcp.mjs +1155 -0
- package/dist/mcp.mjs.map +1 -0
- package/package.json +131 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1360 @@
|
|
|
1
|
+
import { R as RunDeps } from './io-B2EobRzC.cjs';
|
|
2
|
+
export { r as readFileBytes, a as readStreamBytes } from './io-B2EobRzC.cjs';
|
|
3
|
+
import 'node:stream';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The CLI version string, synced with `package.json#version` on release by the Changesets `version`
|
|
7
|
+
* script. Kept in its own module so both the programmatic entry point and the dispatcher share one
|
|
8
|
+
* source of truth.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The `@cosyte/cli` version. On the uniform `v0.0.x-until-first-alpha` ladder.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { VERSION } from "@cosyte/cli";
|
|
18
|
+
*
|
|
19
|
+
* typeof VERSION; // => "string"
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare const VERSION = "0.0.0";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The `cosyte` CLI **exit-code contract**: a designed, documented surface that CI pipelines and
|
|
26
|
+
* shell scripts branch on. Grounded in the Unix `sysexits.h` conventions.
|
|
27
|
+
*
|
|
28
|
+
* The load-bearing rule: **the CLI never prints a reassuring line and exits `0` on input it could not
|
|
29
|
+
* handle.** An undetectable format, an unreadable file, or a parser that throws each map to a
|
|
30
|
+
* distinct, stable non-zero code, never a silent success.
|
|
31
|
+
*
|
|
32
|
+
* | Code | Name | Meaning |
|
|
33
|
+
* |------|------------|----------------------------------------------------------------------|
|
|
34
|
+
* | `0` | `OK` | success: the operation completed; `validate` found the input **valid** |
|
|
35
|
+
* | `1` | `INVALID` | operation-level failure: `validate` found the input **invalid** (a real, expected CI signal: the message is bad, the tool worked) |
|
|
36
|
+
* | `2` | `USAGE` | usage error: unknown command, bad flag, missing argument (EX_USAGE) |
|
|
37
|
+
* | `65` | `DATAERR` | data error: input could not be parsed / format not detected (EX_DATAERR) |
|
|
38
|
+
* | `66` | `NOINPUT` | no input: the file does not exist or is unreadable (EX_NOINPUT) |
|
|
39
|
+
* | `69` | `UNAVAILABLE` | a required capability is not yet available: e.g. `redact` before `@cosyte/deid` ships (EX_UNAVAILABLE) |
|
|
40
|
+
* | `70` | `SOFTWARE` | internal error: an unexpected exception, i.e. a bug (EX_SOFTWARE) |
|
|
41
|
+
*
|
|
42
|
+
* The load-bearing `validate` rule: a **parseable-but-invalid** message is exit `1`, never exit `0`.
|
|
43
|
+
* The CLI must never print a reassuring line and exit green on a bad message. Exit `65` is reserved
|
|
44
|
+
* for input that could not be **parsed** at all (a distinct signal from "parsed, but does not
|
|
45
|
+
* conform").
|
|
46
|
+
*
|
|
47
|
+
* @packageDocumentation
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* The stable exit-code map. Adding a code is a documented, tested change to the CLI's contract;
|
|
51
|
+
* renaming or repurposing one is a breaking change.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* import { EXIT } from "@cosyte/cli";
|
|
56
|
+
*
|
|
57
|
+
* process.exitCode = EXIT.OK; // => 0
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
declare const EXIT: {
|
|
61
|
+
/** Success: the operation completed; `validate` found the input **valid**. */
|
|
62
|
+
readonly OK: 0;
|
|
63
|
+
/** Operation-level failure: `validate` found the input **invalid** (parseable but non-conformant):
|
|
64
|
+
* a real, expected CI signal that the message is bad and the tool worked. Never emitted for
|
|
65
|
+
* unparseable input (that is `DATAERR`). */
|
|
66
|
+
readonly INVALID: 1;
|
|
67
|
+
/** Usage error: unknown command, bad flag, missing argument (`EX_USAGE`). */
|
|
68
|
+
readonly USAGE: 2;
|
|
69
|
+
/** Data error: input could not be parsed or its format could not be detected (`EX_DATAERR`). */
|
|
70
|
+
readonly DATAERR: 65;
|
|
71
|
+
/** No input: the named file does not exist or is unreadable (`EX_NOINPUT`). */
|
|
72
|
+
readonly NOINPUT: 66;
|
|
73
|
+
/** Unavailable: a required capability is not yet built (e.g. `redact` before `@cosyte/deid`), a
|
|
74
|
+
* distinct non-zero signal that is never a fake success (`EX_UNAVAILABLE`). */
|
|
75
|
+
readonly UNAVAILABLE: 69;
|
|
76
|
+
/** Internal error: an unexpected exception (a bug), distinct from a handled bad input (`EX_SOFTWARE`). */
|
|
77
|
+
readonly SOFTWARE: 70;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* A value from {@link EXIT}: the exit code a CLI invocation resolves to.
|
|
81
|
+
*/
|
|
82
|
+
type ExitCode = (typeof EXIT)[keyof typeof EXIT];
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The result of running a CLI invocation, as data: decoupled from `process` so the whole command
|
|
86
|
+
* tree is testable. The thin `bin` entry writes {@link RunResult.stdout} to `process.stdout`,
|
|
87
|
+
* {@link RunResult.stderr} to `process.stderr`, and exits with {@link RunResult.exit}.
|
|
88
|
+
*
|
|
89
|
+
* The two channels carry the CLI's PHI posture: **stdout is the data channel** (the
|
|
90
|
+
* user's explicit request: the parsed model), while **stderr is value-free**: codes and positions
|
|
91
|
+
* only, never an input value.
|
|
92
|
+
*
|
|
93
|
+
* @packageDocumentation
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* A fully-resolved CLI invocation.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* import { EXIT, type RunResult } from "@cosyte/cli";
|
|
102
|
+
*
|
|
103
|
+
* const r: RunResult = { stdout: "{}\n", stderr: "", exit: EXIT.OK };
|
|
104
|
+
* r.exit; // => 0
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
interface RunResult {
|
|
108
|
+
/** The data channel: the parsed model or requested output. May contain the user's data by design. */
|
|
109
|
+
readonly stdout: string;
|
|
110
|
+
/** The diagnostic channel is **value-free**: codes, positions, file paths, format names only. */
|
|
111
|
+
readonly stderr: string;
|
|
112
|
+
/** The exit code the invocation resolves to. */
|
|
113
|
+
readonly exit: ExitCode;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The CLI's **value-free diagnostic channel**. Every error the CLI raises on its own behalf carries a
|
|
118
|
+
* stable `CLI_*` code, a fixed exit code, and a **PHI-safe** message built only from positional /
|
|
119
|
+
* structural context: a segment or field index, a byte offset, a file path, a format name, a warning
|
|
120
|
+
* code. **A diagnostic never echoes an input value** (a name, a DOB, an MRN, a result). This is the
|
|
121
|
+
* parsers' "warning = code + position, never a value" rule applied at the CLI edge.
|
|
122
|
+
*
|
|
123
|
+
* @packageDocumentation
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Stable **CLI diagnostic code** registry: errors the CLI owns (routing, I/O, argument handling),
|
|
128
|
+
* distinct from the wrapped library's own warning/issue codes which are passed through unchanged.
|
|
129
|
+
* Each code is its own value (`key === value`) so the set survives `Object.values(...)`; renaming or
|
|
130
|
+
* removing one is a **breaking change** because scripts branch on the stderr text.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* import { CLI_CODES } from "@cosyte/cli";
|
|
135
|
+
*
|
|
136
|
+
* if (diagnostic.code === CLI_CODES.CLI_FORMAT_UNDETECTED) {
|
|
137
|
+
* // ask the user for --format
|
|
138
|
+
* }
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
declare const CLI_CODES: {
|
|
142
|
+
/** A required argument was missing or a flag was invalid. Exit `2`. */
|
|
143
|
+
readonly CLI_USAGE: "CLI_USAGE";
|
|
144
|
+
/** The named file does not exist or could not be read. Exit `66`. */
|
|
145
|
+
readonly CLI_NO_INPUT: "CLI_NO_INPUT";
|
|
146
|
+
/** The input was empty, no bytes to detect or parse. Exit `65`. */
|
|
147
|
+
readonly CLI_EMPTY_INPUT: "CLI_EMPTY_INPUT";
|
|
148
|
+
/** No format signature matched; the CLI will not guess. Names the candidates, never the bytes. Exit `65`. */
|
|
149
|
+
readonly CLI_FORMAT_UNDETECTED: "CLI_FORMAT_UNDETECTED";
|
|
150
|
+
/** More than one format signature matched; the CLI will not guess. Names the candidates, never the bytes. Exit `65`. */
|
|
151
|
+
readonly CLI_FORMAT_AMBIGUOUS: "CLI_FORMAT_AMBIGUOUS";
|
|
152
|
+
/** A recognised format whose parser does not support the requested operation (e.g. `parse` on a
|
|
153
|
+
* format wired only for `inspect`). Names the format + operation, never the bytes. Exit `65`. */
|
|
154
|
+
readonly CLI_FORMAT_UNSUPPORTED: "CLI_FORMAT_UNSUPPORTED";
|
|
155
|
+
/** The optional parser package for a recognised format is not installed. The six breadth parsers
|
|
156
|
+
* (dicom/x12/ccda/ncpdp/astm/mllp) are `optionalDependencies`: normally installed, but if
|
|
157
|
+
* one is absent the CLI degrades to this value-free signal rather than crashing. Exit `69`. */
|
|
158
|
+
readonly CLI_PARSER_UNAVAILABLE: "CLI_PARSER_UNAVAILABLE";
|
|
159
|
+
/** The wrapped parser rejected the input. Positional context only, never the offending bytes. Exit `65`. */
|
|
160
|
+
readonly CLI_PARSE_FAILED: "CLI_PARSE_FAILED";
|
|
161
|
+
/** The BYO ConceptMap supplied to `map-codes` is not valid JSON or not a loadable FHIR ConceptMap.
|
|
162
|
+
* Names the stable terminology-loader code, never the map's bytes. Exit `65`. */
|
|
163
|
+
readonly CLI_MAP_INVALID: "CLI_MAP_INVALID";
|
|
164
|
+
/** A command whose ground-layer library is not yet built (e.g. `redact` before `@cosyte/deid`
|
|
165
|
+
* ships). Never a fake success: a distinct, value-free "unavailable" signal. Exit `69`. */
|
|
166
|
+
readonly CLI_NOT_IMPLEMENTED: "CLI_NOT_IMPLEMENTED";
|
|
167
|
+
/** An unexpected internal error (a bug). Exit `70`. */
|
|
168
|
+
readonly CLI_INTERNAL: "CLI_INTERNAL";
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* A value from {@link CLI_CODES}: the stable code carried by a {@link CliError}.
|
|
172
|
+
*/
|
|
173
|
+
type CliCode = (typeof CLI_CODES)[keyof typeof CLI_CODES];
|
|
174
|
+
/**
|
|
175
|
+
* A typed, PHI-safe CLI error. Its {@link CliError.message | message} is **value-free by
|
|
176
|
+
* construction**: callers must build it from codes, indices, offsets, format names, and file paths,
|
|
177
|
+
* never from an input value. It carries the {@link ExitCode} the invocation resolves to.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* import { CliError, CLI_CODES, EXIT } from "@cosyte/cli";
|
|
182
|
+
*
|
|
183
|
+
* throw new CliError(CLI_CODES.CLI_NO_INPUT, EXIT.NOINPUT, "cannot read the named file");
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
declare class CliError extends Error {
|
|
187
|
+
/** The stable diagnostic code. */
|
|
188
|
+
readonly code: CliCode;
|
|
189
|
+
/** The exit code this error resolves the invocation to. */
|
|
190
|
+
readonly exit: ExitCode;
|
|
191
|
+
/**
|
|
192
|
+
* @param code - A stable {@link CliCode}.
|
|
193
|
+
* @param exit - The {@link ExitCode} to resolve to.
|
|
194
|
+
* @param message - A **value-free** explanation (positional/structural context only, never PHI).
|
|
195
|
+
*/
|
|
196
|
+
constructor(code: CliCode, exit: ExitCode, message: string);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Render a {@link CliError} as a single value-free `stderr` line: `cosyte: <CODE>: <message>`. The
|
|
200
|
+
* message is already value-free by the {@link CliError} contract; this only prefixes the tool name
|
|
201
|
+
* and the code so scripts and humans can branch on a stable token.
|
|
202
|
+
*
|
|
203
|
+
* @param err - The CLI error to render.
|
|
204
|
+
* @returns The stderr line (no trailing newline).
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* import { CliError, CLI_CODES, EXIT, formatDiagnostic } from "@cosyte/cli";
|
|
208
|
+
*
|
|
209
|
+
* formatDiagnostic(new CliError(CLI_CODES.CLI_USAGE, EXIT.USAGE, "missing <file> argument"));
|
|
210
|
+
* // => "cosyte: CLI_USAGE: missing <file> argument"
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
declare function formatDiagnostic(err: CliError): string;
|
|
214
|
+
/**
|
|
215
|
+
* Resolve a {@link CliError} into a value-free {@link RunResult}: empty `stdout` (nothing reaches the
|
|
216
|
+
* data channel on an error), the rendered diagnostic on `stderr`, and the error's exit code. The
|
|
217
|
+
* single place the CLI turns an owned error into a result, so every command and the dispatcher render
|
|
218
|
+
* errors identically.
|
|
219
|
+
*
|
|
220
|
+
* @param err - The CLI error to resolve.
|
|
221
|
+
* @returns The value-free {@link RunResult}.
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* import { CliError, CLI_CODES, EXIT, errorResult } from "@cosyte/cli";
|
|
225
|
+
*
|
|
226
|
+
* errorResult(new CliError(CLI_CODES.CLI_USAGE, EXIT.USAGE, "missing <file>")).exit; // => 2
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
declare function errorResult(err: CliError): RunResult;
|
|
230
|
+
/**
|
|
231
|
+
* Coerce an unknown thrown value into a {@link CliError}. A {@link CliError} passes through; anything
|
|
232
|
+
* else becomes a `CLI_INTERNAL` / {@link EXIT.SOFTWARE} error whose message is a **fixed, value-free
|
|
233
|
+
* string**: the original message is deliberately discarded so a parser exception that embedded input
|
|
234
|
+
* bytes can never reach stderr.
|
|
235
|
+
*
|
|
236
|
+
* @param err - The caught value.
|
|
237
|
+
* @returns A {@link CliError} safe to render to stderr.
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* import { toCliError } from "@cosyte/cli";
|
|
241
|
+
*
|
|
242
|
+
* toCliError(new Error("boom")).code; // => "CLI_INTERNAL"
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
declare function toCliError(err: unknown): CliError;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* **Content-based format autodetection**: the feature that makes `cosyte parse <file>` feel magic,
|
|
249
|
+
* and the one that must **fail safe**. Detection sniffs the leading bytes' *content*, never the file
|
|
250
|
+
* extension: a `.txt` full of `MSH|^~\&…` is HL7; a `.json` with a `resourceType` is FHIR.
|
|
251
|
+
*
|
|
252
|
+
* The hazard is a wrong sniff routing bytes to the wrong parser and yielding confident garbage. So
|
|
253
|
+
* detection is **conservative**: a single confident signature match parses; **zero or more than one**
|
|
254
|
+
* match is *not* a guess. It is a typed `none`/`ambiguous` result the caller turns into a data-error
|
|
255
|
+
* exit asking for `--format`. This is the parsers' "never a confident wrong value" rule at the
|
|
256
|
+
* routing layer.
|
|
257
|
+
*
|
|
258
|
+
* All eight cosyte formats carry a signature: **hl7** (`MSH` + field separator), **mllp**
|
|
259
|
+
* (a leading `0x0B` VT frame byte: an MLLP-framed stream, de-framed to its enclosed HL7), **fhir** (a
|
|
260
|
+
* JSON object declaring `resourceType`), **x12** (a leading `ISA` interchange header), **astm** (a
|
|
261
|
+
* leading `H` record whose second byte is the field delimiter), **ccda** (a `<ClinicalDocument>` root),
|
|
262
|
+
* **ncpdp** (a `<Message>` root in the NCPDP SCRIPT namespace), and **dicom** (the `DICM` magic at byte
|
|
263
|
+
* 128). The signatures are **conservative and, on realistic inputs, mutually exclusive**: the
|
|
264
|
+
* distinctive-lead formats (`MSH`, `ISA`, `H`+delimiter, `<ClinicalDocument>`, `<Message>`+ncpdp, the
|
|
265
|
+
* `DICM` magic, `0x0B` VT, `{…"resourceType"`) don't overlap on a real message of any one type. They
|
|
266
|
+
* are **not** disjoint in the absolute sense: a pathological input could satisfy two (an MLLP frame
|
|
267
|
+
* enclosing a `<ClinicalDocument>` payload matches both `mllp` and `ccda`; a text file whose byte 128
|
|
268
|
+
* is `DICM` matches both), which is exactly why the **`ambiguous` branch is load-bearing, not dead
|
|
269
|
+
* code**: any co-match is a *detected* ambiguity (a value-free data error asking for `--format`),
|
|
270
|
+
* **never a silent mis-route**.
|
|
271
|
+
*
|
|
272
|
+
* @packageDocumentation
|
|
273
|
+
*/
|
|
274
|
+
|
|
275
|
+
/** The set of formats the `cosyte` command names. */
|
|
276
|
+
type CosyteFormat = "hl7" | "fhir" | "dicom" | "x12" | "ccda" | "ncpdp" | "astm" | "mllp";
|
|
277
|
+
/**
|
|
278
|
+
* How confident autodetection is:
|
|
279
|
+
* - `certain`: exactly one signature matched (`format` names it);
|
|
280
|
+
* - `ambiguous`: more than one matched (`format` is `null`; `candidates` names them);
|
|
281
|
+
* - `none`: nothing matched (`format` is `null`; `candidates` is empty).
|
|
282
|
+
*
|
|
283
|
+
* `format` is `null` unless `confidence` is `certain`, so a non-certain result can never be mistaken
|
|
284
|
+
* for a routable format.
|
|
285
|
+
*/
|
|
286
|
+
interface DetectResult {
|
|
287
|
+
/** The detected format, or `null` when `confidence` is not `certain`. */
|
|
288
|
+
readonly format: CosyteFormat | null;
|
|
289
|
+
/** The detection confidence. */
|
|
290
|
+
readonly confidence: "certain" | "ambiguous" | "none";
|
|
291
|
+
/** The matching candidate formats (0 for `none`, 1 for `certain`, ≥2 for `ambiguous`). Value-free. */
|
|
292
|
+
readonly candidates: readonly CosyteFormat[];
|
|
293
|
+
}
|
|
294
|
+
/** The formats content-autodetection can recognise (every format now carries a signature). */
|
|
295
|
+
declare const DETECTABLE_FORMATS: readonly CosyteFormat[];
|
|
296
|
+
/**
|
|
297
|
+
* Detect the healthcare format of `bytes` by content, conservatively.
|
|
298
|
+
*
|
|
299
|
+
* @param bytes - The input bytes (a whole file or a stdin buffer). Only the leading prefix is sniffed.
|
|
300
|
+
* @returns A {@link DetectResult}: `certain` + a `format` on a single match, else `ambiguous`/`none`
|
|
301
|
+
* with `format: null` and the value-free `candidates` list. **Never guesses.**
|
|
302
|
+
* @example
|
|
303
|
+
* ```ts
|
|
304
|
+
* import { detectFormat } from "@cosyte/cli";
|
|
305
|
+
*
|
|
306
|
+
* const enc = new TextEncoder();
|
|
307
|
+
* detectFormat(enc.encode("MSH|^~\\&|A|B\r")).format; // => "hl7"
|
|
308
|
+
* detectFormat(enc.encode('{"resourceType":"Patient"}')).format; // => "fhir"
|
|
309
|
+
* detectFormat(enc.encode("hello")).confidence; // => "none"
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
declare function detectFormat(bytes: Uint8Array): DetectResult;
|
|
313
|
+
/**
|
|
314
|
+
* Classify a list of matched candidate formats into a {@link DetectResult}: exactly one → `certain`,
|
|
315
|
+
* zero → `none`, two-or-more → `ambiguous` (all with `format` `null` unless certain). Split out and
|
|
316
|
+
* exported so the **ambiguity** contract is directly testable. It is otherwise unreachable while
|
|
317
|
+
* every signature is disjoint, and it must stay a *detected* ambiguity, never a mis-route.
|
|
318
|
+
*
|
|
319
|
+
* @param candidates - The formats whose signatures matched the input.
|
|
320
|
+
* @returns The classified {@link DetectResult}.
|
|
321
|
+
* @example
|
|
322
|
+
* ```ts
|
|
323
|
+
* import { classifyCandidates } from "@cosyte/cli";
|
|
324
|
+
*
|
|
325
|
+
* classifyCandidates(["hl7"]).confidence; // => "certain"
|
|
326
|
+
* classifyCandidates([]).confidence; // => "none"
|
|
327
|
+
* classifyCandidates(["hl7", "fhir"]).confidence; // => "ambiguous"
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
declare function classifyCandidates(candidates: readonly CosyteFormat[]): DetectResult;
|
|
331
|
+
/**
|
|
332
|
+
* Build the value-free {@link CliError} for a **non-certain** detection: the data error the caller
|
|
333
|
+
* returns instead of guessing a parser. `ambiguous` names the matching candidates (a value-free code
|
|
334
|
+
* list); `none` asks for `--format`. Both map to the data-error exit (`65`). Never echoes input bytes.
|
|
335
|
+
*
|
|
336
|
+
* @param detected - A {@link DetectResult} whose `format` is `null` (i.e. not `certain`).
|
|
337
|
+
* @returns A value-free `CLI_FORMAT_AMBIGUOUS` or `CLI_FORMAT_UNDETECTED` error.
|
|
338
|
+
* @example
|
|
339
|
+
* ```ts
|
|
340
|
+
* import { classifyCandidates, detectionError } from "@cosyte/cli";
|
|
341
|
+
*
|
|
342
|
+
* detectionError(classifyCandidates([])).code; // => "CLI_FORMAT_UNDETECTED"
|
|
343
|
+
* detectionError(classifyCandidates(["hl7", "fhir"])).code; // => "CLI_FORMAT_AMBIGUOUS"
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
declare function detectionError(detected: DetectResult): CliError;
|
|
347
|
+
/** The full set of format names `--format` accepts as syntactically valid (a bad value is a usage error). */
|
|
348
|
+
declare const KNOWN_FORMATS: readonly CosyteFormat[];
|
|
349
|
+
/**
|
|
350
|
+
* Narrow an arbitrary `--format` string to a {@link CosyteFormat}, or `null` if it is not a known
|
|
351
|
+
* format name (which the caller turns into a usage error, exit `2`).
|
|
352
|
+
*
|
|
353
|
+
* @param value - The raw `--format` argument.
|
|
354
|
+
* @returns The narrowed format, or `null`.
|
|
355
|
+
* @example
|
|
356
|
+
* ```ts
|
|
357
|
+
* import { asCosyteFormat } from "@cosyte/cli";
|
|
358
|
+
*
|
|
359
|
+
* asCosyteFormat("hl7"); // => "hl7"
|
|
360
|
+
* asCosyteFormat("nope"); // => null
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
declare function asCosyteFormat(value: string): CosyteFormat | null;
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* The CLI's **value-free finding** shape and the positional locators that populate it. A finding says
|
|
367
|
+
* *what kind* of thing was observed (a stable code + severity) and *where* (a positional locator),
|
|
368
|
+
* **never** the offending value. Shared by `validate` (conformance findings) and
|
|
369
|
+
* `inspect` (structural summary) so both render locations identically and value-free.
|
|
370
|
+
*
|
|
371
|
+
* @packageDocumentation
|
|
372
|
+
*/
|
|
373
|
+
/**
|
|
374
|
+
* A single value-free finding: a stable code, a severity, and a positional locator. Every field is
|
|
375
|
+
* safe to print, log, or serialize. None carries a name, DOB, MRN, or field value.
|
|
376
|
+
*/
|
|
377
|
+
interface Finding {
|
|
378
|
+
/** The stable diagnostic/warning code (the wrapped library's or the CLI's). */
|
|
379
|
+
readonly code: string;
|
|
380
|
+
/** The R4-style severity (`fatal` | `error` | `warning` | `information`), or `warning` for HL7. */
|
|
381
|
+
readonly severity: string;
|
|
382
|
+
/** A value-free positional locator: a FHIRPath expression, or an HL7 segment/field index path. */
|
|
383
|
+
readonly location: string;
|
|
384
|
+
}
|
|
385
|
+
/** The minimal, value-free positional shape shared by HL7 warnings (indices only, never a value). */
|
|
386
|
+
interface Hl7PositionLike {
|
|
387
|
+
readonly segmentIndex: number;
|
|
388
|
+
readonly fieldIndex?: number;
|
|
389
|
+
readonly repetitionIndex?: number;
|
|
390
|
+
readonly componentIndex?: number;
|
|
391
|
+
readonly subcomponentIndex?: number;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Render an HL7 warning position as a **value-free** locator string built only from its numeric
|
|
395
|
+
* indices: e.g. `seg[3].field[5].comp[1]`. No segment content, no field value, ever appears; only the
|
|
396
|
+
* structural coordinates the parser reported.
|
|
397
|
+
*
|
|
398
|
+
* @param pos - The HL7 position (segment/field/repetition/component/subcomponent indices).
|
|
399
|
+
* @returns A value-free locator, e.g. `"seg[3].field[5]"`.
|
|
400
|
+
* @example
|
|
401
|
+
* ```ts
|
|
402
|
+
* import { formatHl7Position } from "@cosyte/cli";
|
|
403
|
+
*
|
|
404
|
+
* formatHl7Position({ segmentIndex: 3, fieldIndex: 5 }); // => "seg[3].field[5]"
|
|
405
|
+
* formatHl7Position({ segmentIndex: 0 }); // => "seg[0]"
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
declare function formatHl7Position(pos: Hl7PositionLike): string;
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* The **per-format parser adapter registry**: the single place the CLI routes a resolved format to
|
|
412
|
+
* its wrapped `@cosyte/*` parser, for each of the four wrapping operations (`parse` / `inspect` /
|
|
413
|
+
* `fmt` / `validate`). One lazy, value-free registry, so the wrapper boundary lives in exactly one
|
|
414
|
+
* file.
|
|
415
|
+
*
|
|
416
|
+
* **Capability is per (format, operation).** Not every parser faithfully supports every operation:
|
|
417
|
+
* DICOM's model is binary (no faithful JSON `parse`, no text `fmt`), C-CDA's canonical form is XML (no
|
|
418
|
+
* library-blessed JSON `parse`), MLLP is a transport container (de-framed to HL7, `parse`/`inspect`
|
|
419
|
+
* only). {@link OP_SUPPORT} is the honest matrix; an unsupported (format, op) is a value-free
|
|
420
|
+
* `CLI_FORMAT_UNSUPPORTED`, **never** a faked result.
|
|
421
|
+
*
|
|
422
|
+
* **Lazy + optional.** Every parser is imported dynamically inside its branch, so `cosyte parse msg.hl7`
|
|
423
|
+
* never loads the DICOM or X12 code. The six breadth parsers are `optionalDependencies`: if
|
|
424
|
+
* one is absent the dynamic import is caught and degrades to a value-free `CLI_PARSER_UNAVAILABLE`
|
|
425
|
+
* (exit `69`) rather than an unhandled crash: the CLI core still works with any of them missing.
|
|
426
|
+
*
|
|
427
|
+
* @packageDocumentation
|
|
428
|
+
*/
|
|
429
|
+
|
|
430
|
+
/** The four wrapping operations a command can ask a format's adapter to perform. */
|
|
431
|
+
type Op = "parse" | "inspect" | "fmt" | "validate";
|
|
432
|
+
/** A value-free parse warning: a stable code plus the parser's positional (index-only) context. */
|
|
433
|
+
type ParseWarning = {
|
|
434
|
+
readonly code: string;
|
|
435
|
+
} & Readonly<Record<string, unknown>>;
|
|
436
|
+
/** The result of wrapping a parser's `parse`: the library's parsed model + its value-free warnings. */
|
|
437
|
+
interface ParseResult {
|
|
438
|
+
/** The wrapped library's parsed model: emitted verbatim as JSON on the data channel. */
|
|
439
|
+
readonly model: unknown;
|
|
440
|
+
/** The parser's value-free warnings (code + position). */
|
|
441
|
+
readonly warnings: readonly ParseWarning[];
|
|
442
|
+
}
|
|
443
|
+
/** The result of a canonical re-serialization (`fmt`): the spec-clean text + a value-free warning count. */
|
|
444
|
+
interface FmtResult {
|
|
445
|
+
/** The wrapped serializer's output text. */
|
|
446
|
+
readonly output: string;
|
|
447
|
+
/** How many parse warnings the round-trip surfaced (value-free count only). */
|
|
448
|
+
readonly warningCount: number;
|
|
449
|
+
}
|
|
450
|
+
/** The verdict of running a format's validation surface: value-free findings + the validity boolean. */
|
|
451
|
+
interface Verdict {
|
|
452
|
+
/** The value-free findings (code + severity + positional locator). */
|
|
453
|
+
readonly findings: readonly Finding[];
|
|
454
|
+
/** `true` iff the input is valid (no error/fatal-severity finding). */
|
|
455
|
+
readonly valid: boolean;
|
|
456
|
+
}
|
|
457
|
+
/** Value-free structural summary of an HL7 v2 message. */
|
|
458
|
+
interface Hl7Summary {
|
|
459
|
+
readonly format: "hl7";
|
|
460
|
+
readonly messageType: string | null;
|
|
461
|
+
readonly version: string | null;
|
|
462
|
+
readonly segmentCount: number;
|
|
463
|
+
readonly segments: Readonly<Record<string, number>>;
|
|
464
|
+
readonly warningCount: number;
|
|
465
|
+
}
|
|
466
|
+
/** Value-free structural summary of a FHIR resource. */
|
|
467
|
+
interface FhirSummary {
|
|
468
|
+
readonly format: "fhir";
|
|
469
|
+
readonly resourceType: string | null;
|
|
470
|
+
readonly bundleType?: string | null;
|
|
471
|
+
readonly entryCount?: number;
|
|
472
|
+
readonly entryResourceTypes?: Readonly<Record<string, number>>;
|
|
473
|
+
readonly issueCount: number;
|
|
474
|
+
}
|
|
475
|
+
/** Value-free structural summary of an X12 interchange (envelope counts + transaction-set ids). */
|
|
476
|
+
interface X12Summary {
|
|
477
|
+
readonly format: "x12";
|
|
478
|
+
readonly groupCount: number;
|
|
479
|
+
readonly transactionCount: number;
|
|
480
|
+
/** Count of each transaction-set identifier code (ST-01), e.g. `{ "834": 1 }`: value-free codes. */
|
|
481
|
+
readonly transactionSetIds: Readonly<Record<string, number>>;
|
|
482
|
+
readonly segmentCount: number;
|
|
483
|
+
readonly warningCount: number;
|
|
484
|
+
}
|
|
485
|
+
/** Value-free structural summary of an ASTM record message. */
|
|
486
|
+
interface AstmSummary {
|
|
487
|
+
readonly format: "astm";
|
|
488
|
+
/** The host-query classification kind (`results` | `orders` | `host-query` | `indeterminate`). */
|
|
489
|
+
readonly messageKind: string;
|
|
490
|
+
readonly recordCount: number;
|
|
491
|
+
/** Count of each single-letter record type, e.g. `{ H: 1, P: 1, O: 2, R: 4, L: 1 }`. */
|
|
492
|
+
readonly recordTypes: Readonly<Record<string, number>>;
|
|
493
|
+
readonly warningCount: number;
|
|
494
|
+
}
|
|
495
|
+
/** Value-free structural summary of a C-CDA document (document type + section LOINC codes). */
|
|
496
|
+
interface CcdaSummary {
|
|
497
|
+
readonly format: "ccda";
|
|
498
|
+
readonly documentType: string | null;
|
|
499
|
+
readonly sectionCount: number;
|
|
500
|
+
/** The LOINC `code` of each top-level section (value-free classification codes). */
|
|
501
|
+
readonly sectionCodes: readonly string[];
|
|
502
|
+
readonly warningCount: number;
|
|
503
|
+
}
|
|
504
|
+
/** Value-free structural summary of a DICOM dataset (classification UIDs + element count). */
|
|
505
|
+
interface DicomSummary {
|
|
506
|
+
readonly format: "dicom";
|
|
507
|
+
/** The SOP Class UID (a standard OID identifying the object type: value-free classification). */
|
|
508
|
+
readonly sopClassUid: string | null;
|
|
509
|
+
/** The Transfer Syntax UID (encoding classification: value-free). */
|
|
510
|
+
readonly transferSyntaxUid: string | null;
|
|
511
|
+
readonly elementCount: number;
|
|
512
|
+
readonly warningCount: number;
|
|
513
|
+
}
|
|
514
|
+
/** Value-free structural summary of an NCPDP SCRIPT message. */
|
|
515
|
+
interface NcpdpSummary {
|
|
516
|
+
readonly format: "ncpdp";
|
|
517
|
+
readonly standard: "SCRIPT";
|
|
518
|
+
/** The SCRIPT message type (e.g. `NewRx`, `CancelRx`: a value-free classification). */
|
|
519
|
+
readonly messageType: string | null;
|
|
520
|
+
/** The SCRIPT version (e.g. `2017071`: value-free). */
|
|
521
|
+
readonly version: string | null;
|
|
522
|
+
readonly warningCount: number;
|
|
523
|
+
}
|
|
524
|
+
/** Value-free structural summary of an MLLP stream (frame count only; the payloads are HL7). */
|
|
525
|
+
interface MllpSummary {
|
|
526
|
+
readonly format: "mllp";
|
|
527
|
+
readonly frameCount: number;
|
|
528
|
+
readonly warningCount: number;
|
|
529
|
+
}
|
|
530
|
+
/** The discriminated value-free structural summary `inspect` renders. */
|
|
531
|
+
type InspectSummary = Hl7Summary | FhirSummary | X12Summary | AstmSummary | CcdaSummary | DicomSummary | NcpdpSummary | MllpSummary;
|
|
532
|
+
/**
|
|
533
|
+
* The honest per-format operation matrix. A (format, op) pair absent here is a value-free
|
|
534
|
+
* `CLI_FORMAT_UNSUPPORTED`, never a faked result. The unsupported cells and why:
|
|
535
|
+
* - **dicom** `parse`/`fmt`. The model is binary (no faithful JSON view; `serializeDicom` emits a
|
|
536
|
+
* Part-10 byte stream, not text). `inspect`/`validate` are supported.
|
|
537
|
+
* - **ccda** `parse`: the canonical form is XML; there is no library-blessed JSON model, so `fmt`
|
|
538
|
+
* (XML re-serialize) is the faithful surface. `inspect`/`fmt`/`validate` are supported.
|
|
539
|
+
* - **mllp** `fmt`/`validate`: MLLP is a transport container the CLI de-frames to HL7; `parse` yields
|
|
540
|
+
* the enclosed HL7 message(s) and `inspect` reports the frame count.
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```ts
|
|
544
|
+
* import { OP_SUPPORT } from "@cosyte/cli";
|
|
545
|
+
*
|
|
546
|
+
* OP_SUPPORT.dicom.has("parse"); // => false
|
|
547
|
+
* OP_SUPPORT.hl7.has("validate"); // => true
|
|
548
|
+
* ```
|
|
549
|
+
*/
|
|
550
|
+
declare const OP_SUPPORT: Readonly<Record<CosyteFormat, ReadonlySet<Op>>>;
|
|
551
|
+
/**
|
|
552
|
+
* True iff `format`'s wrapped parser supports `op` in this build (see {@link OP_SUPPORT}).
|
|
553
|
+
*
|
|
554
|
+
* @param format - The resolved format.
|
|
555
|
+
* @param op - The wrapping operation.
|
|
556
|
+
* @returns Whether the (format, op) pair is wired.
|
|
557
|
+
* @example
|
|
558
|
+
* ```ts
|
|
559
|
+
* import { supportsOp } from "@cosyte/cli";
|
|
560
|
+
*
|
|
561
|
+
* supportsOp("x12", "fmt"); // => true
|
|
562
|
+
* supportsOp("dicom", "parse"); // => false
|
|
563
|
+
* ```
|
|
564
|
+
*/
|
|
565
|
+
declare function supportsOp(format: CosyteFormat, op: Op): boolean;
|
|
566
|
+
/**
|
|
567
|
+
* The formats that support `op`, as a sorted, value-free list: used to build the "does not support"
|
|
568
|
+
* diagnostic so the user is told which formats *do* support the operation they asked for.
|
|
569
|
+
*
|
|
570
|
+
* @param op - The wrapping operation.
|
|
571
|
+
* @returns The supporting format names, sorted.
|
|
572
|
+
* @example
|
|
573
|
+
* ```ts
|
|
574
|
+
* import { formatsSupporting } from "@cosyte/cli";
|
|
575
|
+
*
|
|
576
|
+
* formatsSupporting("fmt"); // => ["astm", "ccda", "fhir", "hl7", "ncpdp", "x12"]
|
|
577
|
+
* ```
|
|
578
|
+
*/
|
|
579
|
+
declare function formatsSupporting(op: Op): readonly CosyteFormat[];
|
|
580
|
+
/**
|
|
581
|
+
* Render an arbitrary parser **position** object as a value-free locator string, keeping **only**
|
|
582
|
+
* number-valued own properties (the parsers' positions are index-only): e.g. `{ segmentIndex: 3,
|
|
583
|
+
* elementIndex: 2 }` → `segmentIndex[3].elementIndex[2]`. A non-number field (which a position should
|
|
584
|
+
* never carry) is dropped, so a stray value can never reach a diagnostic. Falls back to `"?"` when no
|
|
585
|
+
* numeric index is present.
|
|
586
|
+
*
|
|
587
|
+
* @param pos - A parser position object (index-only by the parser's contract).
|
|
588
|
+
* @returns A value-free locator built solely from numeric indices.
|
|
589
|
+
* @example
|
|
590
|
+
* ```ts
|
|
591
|
+
* import { valueFreeLocator } from "@cosyte/cli";
|
|
592
|
+
*
|
|
593
|
+
* valueFreeLocator({ segmentIndex: 3, elementIndex: 2 }); // => "segmentIndex[3].elementIndex[2]"
|
|
594
|
+
* valueFreeLocator({}); // => "?"
|
|
595
|
+
* ```
|
|
596
|
+
*/
|
|
597
|
+
declare function valueFreeLocator(pos: unknown): string;
|
|
598
|
+
/**
|
|
599
|
+
* Lazy-import an **optional** parser package, mapping an *absent* package to a value-free
|
|
600
|
+
* `CLI_PARSER_UNAVAILABLE` (exit `69`): the graceful-degradation path for the `optionalDependencies`
|
|
601
|
+
* breadth parsers. A genuine import that succeeds passes straight through; any error whose
|
|
602
|
+
* shape is "module not found" becomes the value-free CLI error. Other errors propagate unchanged.
|
|
603
|
+
*
|
|
604
|
+
* @template T - The imported module's type.
|
|
605
|
+
* @param format - The format whose optional parser is being loaded (named in the diagnostic).
|
|
606
|
+
* @param load - A thunk performing the dynamic import.
|
|
607
|
+
* @returns The imported module.
|
|
608
|
+
* @throws {CliError} `CLI_PARSER_UNAVAILABLE` (exit `69`) when the package is absent; any other error
|
|
609
|
+
* propagates unchanged.
|
|
610
|
+
* @example
|
|
611
|
+
* ```ts
|
|
612
|
+
* import { loadOptional } from "@cosyte/cli";
|
|
613
|
+
*
|
|
614
|
+
* // a present parser resolves; an absent one throws CLI_PARSER_UNAVAILABLE
|
|
615
|
+
* await loadOptional("x12", () => import("@cosyte/x12"));
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
618
|
+
declare function loadOptional<T>(format: CosyteFormat, load: () => Promise<T>): Promise<T>;
|
|
619
|
+
/**
|
|
620
|
+
* Parse one record of `format` into the wrapped library's model + its value-free warnings. The model
|
|
621
|
+
* is emitted verbatim on the data channel; the CLI adds no parsing of its own.
|
|
622
|
+
*
|
|
623
|
+
* @param format - A format for which `supportsOp(format, "parse")` is true.
|
|
624
|
+
* @param bytes - The single record's bytes (already de-framed for MLLP: callers pass `"hl7"` there).
|
|
625
|
+
* @returns The parsed model + value-free warnings.
|
|
626
|
+
* @throws {CliError} `CLI_PARSER_UNAVAILABLE` if the optional parser is absent; the wrapped parser's
|
|
627
|
+
* own rejection propagates for the command's value-free failure boundary to catch.
|
|
628
|
+
* @example
|
|
629
|
+
* ```ts
|
|
630
|
+
* import { parseFormat } from "@cosyte/cli";
|
|
631
|
+
*
|
|
632
|
+
* const bytes = new TextEncoder().encode('{"resourceType":"Patient","id":"x"}');
|
|
633
|
+
* (await parseFormat("fhir", bytes)).warnings.length; // => 0
|
|
634
|
+
* ```
|
|
635
|
+
*/
|
|
636
|
+
declare function parseFormat(format: CosyteFormat, bytes: Uint8Array): Promise<ParseResult>;
|
|
637
|
+
/**
|
|
638
|
+
* Build the value-free structural summary of one input of `format`: counts and structural type codes
|
|
639
|
+
* only, never a field value.
|
|
640
|
+
*
|
|
641
|
+
* @param format - A format for which `supportsOp(format, "inspect")` is true.
|
|
642
|
+
* @param bytes - The input bytes.
|
|
643
|
+
* @returns A {@link InspectSummary} variant for the format.
|
|
644
|
+
* @throws {CliError} `CLI_PARSER_UNAVAILABLE` if the optional parser is absent; the wrapped parser's
|
|
645
|
+
* rejection propagates for the command's value-free failure boundary.
|
|
646
|
+
* @example
|
|
647
|
+
* ```ts
|
|
648
|
+
* import { inspectFormat } from "@cosyte/cli";
|
|
649
|
+
*
|
|
650
|
+
* const bytes = new TextEncoder().encode('{"resourceType":"Patient"}');
|
|
651
|
+
* (await inspectFormat("fhir", bytes)).format; // => "fhir"
|
|
652
|
+
* ```
|
|
653
|
+
*/
|
|
654
|
+
declare function inspectFormat(format: CosyteFormat, bytes: Uint8Array): Promise<InspectSummary>;
|
|
655
|
+
/**
|
|
656
|
+
* Canonically re-serialize one input of `format` via the wrapped library's spec-clean serializer. The
|
|
657
|
+
* CLI never re-canonicalizes on its own; the output is exactly the serializer's.
|
|
658
|
+
*
|
|
659
|
+
* @param format - A format for which `supportsOp(format, "fmt")` is true.
|
|
660
|
+
* @param bytes - The input bytes.
|
|
661
|
+
* @returns The serialized text + a value-free warning count.
|
|
662
|
+
* @throws {CliError} `CLI_PARSER_UNAVAILABLE` if the optional parser is absent; the wrapped parser's
|
|
663
|
+
* rejection propagates for the command's value-free failure boundary (no partial emit).
|
|
664
|
+
* @example
|
|
665
|
+
* ```ts
|
|
666
|
+
* import { fmtFormat } from "@cosyte/cli";
|
|
667
|
+
*
|
|
668
|
+
* const bytes = new TextEncoder().encode('{ "resourceType":"Patient" , "id":"x" }');
|
|
669
|
+
* (await fmtFormat("fhir", bytes)).output.startsWith("{"); // => true
|
|
670
|
+
* ```
|
|
671
|
+
*/
|
|
672
|
+
declare function fmtFormat(format: CosyteFormat, bytes: Uint8Array): Promise<FmtResult>;
|
|
673
|
+
/**
|
|
674
|
+
* Run the format's validation surface and return a value-free {@link Verdict}. For the Postel's-Law
|
|
675
|
+
* lenient parsers, a **parseable** input is valid: recovered deviations are non-fatal warnings the
|
|
676
|
+
* library surfaces, never a stricter verdict the CLI invents. FHIR additionally runs `validateResource`
|
|
677
|
+
* and is invalid on any error/fatal-severity issue.
|
|
678
|
+
*
|
|
679
|
+
* @param format - A format for which `supportsOp(format, "validate")` is true.
|
|
680
|
+
* @param bytes - The input bytes.
|
|
681
|
+
* @returns The value-free verdict (findings + validity).
|
|
682
|
+
* @throws {CliError} `CLI_PARSER_UNAVAILABLE` if the optional parser is absent; an unparseable input
|
|
683
|
+
* propagates as the wrapped parser's throw for the command to map to a data error (`65`).
|
|
684
|
+
* @example
|
|
685
|
+
* ```ts
|
|
686
|
+
* import { validateFormat } from "@cosyte/cli";
|
|
687
|
+
*
|
|
688
|
+
* const bytes = new TextEncoder().encode('{"resourceType":"Patient","gender":"male"}');
|
|
689
|
+
* (await validateFormat("fhir", bytes)).valid; // => true
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
692
|
+
declare function validateFormat(format: CosyteFormat, bytes: Uint8Array): Promise<Verdict>;
|
|
693
|
+
/**
|
|
694
|
+
* De-frame an MLLP byte stream into its enclosed HL7 payloads via `@cosyte/mllp`'s `FrameReader`. MLLP
|
|
695
|
+
* is a transport container, not a document format: each frame's payload is an HL7 v2 message the CLI
|
|
696
|
+
* then parses/inspects with the `hl7` adapter. Multi-frame streams are the CLI's multi-message surface.
|
|
697
|
+
*
|
|
698
|
+
* @param bytes - The MLLP-framed input.
|
|
699
|
+
* @returns The de-framed HL7 payloads (in stream order) + a value-free framing-warning count.
|
|
700
|
+
* **Truncation is a data error, never a silent drop.** The `FrameReader` is a streaming decoder: an
|
|
701
|
+
* unterminated trailing frame (a VT opened with no closing FS/CR) is left buffered and delivered by
|
|
702
|
+
* *no* callback, so feeding a whole file and reading only `onFrame` would silently lose the partial
|
|
703
|
+
* message with a green exit. We therefore detect an open trailing frame at the byte level (the last
|
|
704
|
+
* VT sits after the last FS: MLLP payloads are HL7 v2 text and never carry the `0x0B`/`0x1C` framing
|
|
705
|
+
* bytes) and reject the whole stream as a value-free `CLI_PARSE_FAILED` data error.
|
|
706
|
+
*
|
|
707
|
+
* @throws {CliError} `CLI_PARSER_UNAVAILABLE` if `@cosyte/mllp` is absent; `CLI_PARSE_FAILED` (exit
|
|
708
|
+
* `65`) on a truncated stream; a hard framing error propagates for the caller's value-free boundary.
|
|
709
|
+
* @example
|
|
710
|
+
* ```ts
|
|
711
|
+
* import { deframeMllp } from "@cosyte/cli";
|
|
712
|
+
*
|
|
713
|
+
* // a VT-framed "MSH|..." message → one payload
|
|
714
|
+
* (await deframeMllp(new Uint8Array([0x0b, 0x4d, 0x53, 0x48, 0x1c, 0x0d]))).payloads.length; // => 1
|
|
715
|
+
* ```
|
|
716
|
+
*/
|
|
717
|
+
declare function deframeMllp(bytes: Uint8Array): Promise<{
|
|
718
|
+
payloads: Buffer[];
|
|
719
|
+
warningCount: number;
|
|
720
|
+
}>;
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* The shared **input + format resolution** every file-consuming command runs before it touches a
|
|
724
|
+
* wrapped parser: resolve the `<file|->` argument, read its bytes (a file, or stdin for `-`), reject
|
|
725
|
+
* empty input, then resolve the format: an explicit `--format` override (validated) or conservative
|
|
726
|
+
* content autodetection, and confirm the format is actually wired in this build. Factored out of the
|
|
727
|
+
* commands so `parse`/`validate`/`inspect`/`fmt` share one identical, value-free front door and the
|
|
728
|
+
* exit-code contract is applied in exactly one place.
|
|
729
|
+
*
|
|
730
|
+
* Every failure is a value-free {@link CliError} rendered to a {@link RunResult}: a missing argument
|
|
731
|
+
* is a usage error (`2`), an unreadable file a no-input error (`66`), empty/undetected/unwired input a
|
|
732
|
+
* data error (`65`). None ever echoes an input byte.
|
|
733
|
+
*
|
|
734
|
+
* @packageDocumentation
|
|
735
|
+
*/
|
|
736
|
+
|
|
737
|
+
/** A successfully-resolved input: the format (guaranteed to support the requested op) and the bytes. */
|
|
738
|
+
interface ResolvedInput {
|
|
739
|
+
/** The resolved format: guaranteed to satisfy `supportsOp(format, op)` for the requested op. */
|
|
740
|
+
readonly format: CosyteFormat;
|
|
741
|
+
/** The input bytes (a whole file or a drained stdin buffer); guaranteed non-empty. */
|
|
742
|
+
readonly bytes: Uint8Array;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* The outcome of {@link resolveInput}: either the resolved input, or a ready-to-return value-free
|
|
746
|
+
* {@link RunResult} carrying the diagnostic + exit code for whatever went wrong. A discriminated union
|
|
747
|
+
* so a command reads `if (!r.ok) return r.result;` and then works with `r.input`.
|
|
748
|
+
*/
|
|
749
|
+
type InputResolution = {
|
|
750
|
+
readonly ok: true;
|
|
751
|
+
readonly input: ResolvedInput;
|
|
752
|
+
} | {
|
|
753
|
+
readonly ok: false;
|
|
754
|
+
readonly result: RunResult;
|
|
755
|
+
};
|
|
756
|
+
/**
|
|
757
|
+
* Resolve and read the input, and resolve its format, for a file-consuming command.
|
|
758
|
+
*
|
|
759
|
+
* @param source - The positional `<file|->` argument (or `undefined` when it was omitted).
|
|
760
|
+
* @param formatOverride - The raw `--format` value, or `undefined` to autodetect by content.
|
|
761
|
+
* @param deps - Injected input readers ({@link RunDeps}).
|
|
762
|
+
* @param op - The wrapping operation the caller will run; the resolved format is confirmed to support
|
|
763
|
+
* it (else a value-free `CLI_FORMAT_UNSUPPORTED` naming the supporting formats).
|
|
764
|
+
* @returns `{ ok: true, input }` when the bytes read and the format resolved to a parser supporting
|
|
765
|
+
* `op`; else `{ ok: false, result }` with a value-free usage/no-input/data-error {@link RunResult}.
|
|
766
|
+
* @throws Propagates a **non-`CliError`** read failure unchanged, so the dispatcher maps it to
|
|
767
|
+
* `CLI_INTERNAL` (a `CliError` read failure, e.g. a missing file, is caught and returned).
|
|
768
|
+
* @example
|
|
769
|
+
* ```ts
|
|
770
|
+
* import { resolveInput } from "@cosyte/cli";
|
|
771
|
+
*
|
|
772
|
+
* const deps = {
|
|
773
|
+
* readFile: async () => new TextEncoder().encode('{"resourceType":"Patient"}'),
|
|
774
|
+
* readStdin: async () => new Uint8Array(),
|
|
775
|
+
* };
|
|
776
|
+
* const r = await resolveInput("patient.json", undefined, deps, "parse");
|
|
777
|
+
* if (r.ok) r.input.format; // => "fhir"
|
|
778
|
+
* ```
|
|
779
|
+
*/
|
|
780
|
+
declare function resolveInput(source: string | undefined, formatOverride: string | undefined, deps: RunDeps, op: Op): Promise<InputResolution>;
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* The CLI's **global PHI display posture**: the single mechanism that gates whether *any* input
|
|
784
|
+
* value may appear on a **secondary surface** (stderr, a diagnostic, a warning, a log line). The CLI
|
|
785
|
+
* is **value-free by default**: every secondary surface carries only positional/structural context
|
|
786
|
+
* (a stable code, a segment/field index, a byte offset, a file path, a format name), never a name, a
|
|
787
|
+
* DOB, an MRN, or a field value. `stdout` is the **data channel** (the user's explicit request) and
|
|
788
|
+
* is exempt: the posture governs everything *but* stdout.
|
|
789
|
+
*
|
|
790
|
+
* `--unsafe-show-values` is the **single, explicit, loud door** to a value on a secondary surface. It
|
|
791
|
+
* is off by default, PHI-exposing by design, and its effect flows through exactly one chokepoint here
|
|
792
|
+
* ({@link unsafeInputSuffix}) so the "a value appears on a secondary surface **iff** the flag is set"
|
|
793
|
+
* property is provable in one place rather than scattered across commands.
|
|
794
|
+
*
|
|
795
|
+
* @packageDocumentation
|
|
796
|
+
*/
|
|
797
|
+
/**
|
|
798
|
+
* The resolved PHI display posture for one invocation. `showValues` is `true` **only** when the user
|
|
799
|
+
* passed `--unsafe-show-values`; every value-echoing decision reads this one flag.
|
|
800
|
+
*/
|
|
801
|
+
interface PhiPosture {
|
|
802
|
+
/** `true` iff `--unsafe-show-values` was given: a value may appear on a secondary surface. */
|
|
803
|
+
readonly showValues: boolean;
|
|
804
|
+
}
|
|
805
|
+
/** The default, safe posture: no value ever reaches a secondary surface. */
|
|
806
|
+
declare const VALUE_FREE: PhiPosture;
|
|
807
|
+
/** The opted-in, PHI-exposing posture selected by `--unsafe-show-values`. */
|
|
808
|
+
declare const SHOW_VALUES: PhiPosture;
|
|
809
|
+
/** The single global flag token that opts into showing values on secondary surfaces. */
|
|
810
|
+
declare const UNSAFE_SHOW_VALUES_FLAG = "--unsafe-show-values";
|
|
811
|
+
/**
|
|
812
|
+
* The maximum number of leading input bytes an `--unsafe-show-values` diagnostic may echo. Bounded so
|
|
813
|
+
* an unsafe excerpt stays a single, readable diagnostic line rather than dumping a whole message.
|
|
814
|
+
*/
|
|
815
|
+
declare const UNSAFE_EXCERPT_MAX = 200;
|
|
816
|
+
/**
|
|
817
|
+
* Resolve the global `--unsafe-show-values` flag out of an argument vector, **order-independently**
|
|
818
|
+
* (it may appear before or after the subcommand), and return the posture plus the argv with every
|
|
819
|
+
* occurrence of the flag removed, so each command's own `parseArgs` never sees it and cannot reject
|
|
820
|
+
* it as unknown. This is the one place the flag is recognised.
|
|
821
|
+
*
|
|
822
|
+
* @param argv - The raw arguments (after the program name).
|
|
823
|
+
* @returns The resolved {@link PhiPosture} and the flag-stripped argv.
|
|
824
|
+
* @example
|
|
825
|
+
* ```ts
|
|
826
|
+
* import { extractPhiPosture } from "@cosyte/cli";
|
|
827
|
+
*
|
|
828
|
+
* extractPhiPosture(["parse", "x.hl7"]).posture.showValues; // => false
|
|
829
|
+
* extractPhiPosture(["--unsafe-show-values", "parse", "x.hl7"]).posture.showValues; // => true
|
|
830
|
+
* extractPhiPosture(["parse", "x.hl7", "--unsafe-show-values"]).argv; // => ["parse", "x.hl7"]
|
|
831
|
+
* ```
|
|
832
|
+
*/
|
|
833
|
+
declare function extractPhiPosture(argv: readonly string[]): {
|
|
834
|
+
posture: PhiPosture;
|
|
835
|
+
argv: string[];
|
|
836
|
+
};
|
|
837
|
+
/**
|
|
838
|
+
* Build the **only** value-bearing addition the CLI ever appends to a secondary (stderr) surface: a
|
|
839
|
+
* bounded, single-line excerpt of the offending input, shown **iff** `--unsafe-show-values` is set.
|
|
840
|
+
* Under the default value-free posture it returns the empty string, so a diagnostic stays value-free
|
|
841
|
+
* unless the user explicitly opted in. This is the single chokepoint the PHI-leak matrix pins.
|
|
842
|
+
*
|
|
843
|
+
* @param bytes - The input the CLI is operating on (only the leading {@link UNSAFE_EXCERPT_MAX} bytes
|
|
844
|
+
* are considered).
|
|
845
|
+
* @param posture - The resolved {@link PhiPosture}.
|
|
846
|
+
* @returns `""` under the value-free default; otherwise a ` [unsafe-show-values] …` suffix carrying a
|
|
847
|
+
* bounded, newline-flattened prefix of `bytes`.
|
|
848
|
+
* @example
|
|
849
|
+
* ```ts
|
|
850
|
+
* import { unsafeInputSuffix, VALUE_FREE, SHOW_VALUES } from "@cosyte/cli";
|
|
851
|
+
*
|
|
852
|
+
* const bytes = new TextEncoder().encode("bad message");
|
|
853
|
+
* unsafeInputSuffix(bytes, VALUE_FREE); // => ""
|
|
854
|
+
* unsafeInputSuffix(bytes, SHOW_VALUES); // => " [unsafe-show-values] offending input …: bad message"
|
|
855
|
+
* ```
|
|
856
|
+
*/
|
|
857
|
+
declare function unsafeInputSuffix(bytes: Uint8Array, posture: PhiPosture): string;
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* The **wrapped-parser failure boundary**: the single, value-free way every command turns a thrown
|
|
861
|
+
* parser/codec exception into a {@link RunResult}. A wrapped library raises a fatal on genuinely
|
|
862
|
+
* unparseable input; its message (and, for HL7, its `snippet`) can embed the offending bytes, so the
|
|
863
|
+
* CLI **never** surfaces that message. It surfaces only a stable, PHI-free code token (via
|
|
864
|
+
* {@link extractStableCode}) plus the format name: the parsers' "warning = code + position, never a
|
|
865
|
+
* value" rule at the CLI edge.
|
|
866
|
+
*
|
|
867
|
+
* The one, opt-in exception is `--unsafe-show-values`, whose bounded excerpt is appended through the
|
|
868
|
+
* lone chokepoint in `core/phi.ts` ({@link unsafeInputSuffix}), so the "a value appears on a
|
|
869
|
+
* secondary surface **iff** the flag is set" property stays provable in one place, uniformly across
|
|
870
|
+
* `parse`/`validate`/`inspect`/`fmt`.
|
|
871
|
+
*
|
|
872
|
+
* @packageDocumentation
|
|
873
|
+
*/
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Pull a `code` off a thrown value **only** when it is a PHI-free constant token
|
|
877
|
+
* (`^[A-Z][A-Z0-9_]*$`: a letter-led UPPER_SNAKE code like `MALFORMED_JSON`), e.g. a wrapped
|
|
878
|
+
* parser's stable fatal code. Anything else (no `code`, a non-string, a non-token, or a **pure-digit**
|
|
879
|
+
* value that could be a raw identifier) yields `null`, so a parser exception that embedded input bytes
|
|
880
|
+
* in a `code`-shaped field can never reach a diagnostic.
|
|
881
|
+
*
|
|
882
|
+
* @param e - A caught value.
|
|
883
|
+
* @returns The stable code token, or `null`.
|
|
884
|
+
* @example
|
|
885
|
+
* ```ts
|
|
886
|
+
* import { extractStableCode } from "@cosyte/cli";
|
|
887
|
+
*
|
|
888
|
+
* extractStableCode({ code: "MALFORMED_JSON" }); // => "MALFORMED_JSON"
|
|
889
|
+
* extractStableCode(new Error("boom")); // => null
|
|
890
|
+
* ```
|
|
891
|
+
*/
|
|
892
|
+
declare function extractStableCode(e: unknown): string | null;
|
|
893
|
+
/**
|
|
894
|
+
* Build the value-free {@link RunResult} for a **wrapped-parser rejection**: a `CLI_PARSE_FAILED`
|
|
895
|
+
* data error (exit `65`). The stderr line names the format and, when the thrown value carried one, a
|
|
896
|
+
* stable code token in parentheses; it **never** echoes the parser's message or the input bytes. Under
|
|
897
|
+
* `--unsafe-show-values` (and only then) a bounded excerpt of the offending input is appended via the
|
|
898
|
+
* single {@link unsafeInputSuffix} chokepoint.
|
|
899
|
+
*
|
|
900
|
+
* @param format - The format whose parser rejected the input.
|
|
901
|
+
* @param bytes - The offending input (only consulted for the opt-in unsafe excerpt).
|
|
902
|
+
* @param posture - The resolved {@link PhiPosture}; the excerpt is appended only when it opts in.
|
|
903
|
+
* @param e - The caught exception (its `code`, if a stable token, is surfaced: nothing else).
|
|
904
|
+
* @returns A value-free `CLI_PARSE_FAILED` / exit-`65` {@link RunResult}.
|
|
905
|
+
* @example
|
|
906
|
+
* ```ts
|
|
907
|
+
* import { parseFailureResult, VALUE_FREE } from "@cosyte/cli";
|
|
908
|
+
*
|
|
909
|
+
* const bytes = new TextEncoder().encode("not hl7");
|
|
910
|
+
* parseFailureResult("hl7", bytes, VALUE_FREE, { code: "MISSING_MSH" }).exit; // => 65
|
|
911
|
+
* ```
|
|
912
|
+
*/
|
|
913
|
+
declare function parseFailureResult(format: CosyteFormat, bytes: Uint8Array, posture: PhiPosture, e: unknown): RunResult;
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* The **de-identification seam**: the single, documented plug-in point where the `redact` / `deid`
|
|
917
|
+
* command will delegate to **`@cosyte/deid`** once that library ships and is vetted.
|
|
918
|
+
*
|
|
919
|
+
* ## Why the CLI ships no built-in redactor (the boundary decision)
|
|
920
|
+
*
|
|
921
|
+
* `@cosyte/deid` is **unpublished**, and the wrapped
|
|
922
|
+
* parsers (`@cosyte/hl7`, `@cosyte/fhir`) expose **no de-identification API**: they redact only
|
|
923
|
+
* their own *diagnostics*, not the parsed model. So there is nothing to delegate to today, and adding
|
|
924
|
+
* `@cosyte/deid` would depend on unbuilt code.
|
|
925
|
+
*
|
|
926
|
+
* A tempting alternative, a "minimal built-in Safe-Harbor pass over the obvious PHI loci" (e.g. the
|
|
927
|
+
* HL7 `PID` segment, `Patient.name`/`Patient.address` in FHIR), is **deliberately rejected**. Real
|
|
928
|
+
* messages carry PHI far beyond those loci: HL7 `NK1`/`GT1`/`IN1`/`OBX`/`NTE`, FHIR extensions and
|
|
929
|
+
* contained resources, free-text notes. A redactor that scrubs the obvious fields and emits output
|
|
930
|
+
* that *looks* de-identified while silently leaving PHI behind is a **false-safety impression**. A
|
|
931
|
+
* `redact` that under-redacts is worse than no `redact`.
|
|
932
|
+
*
|
|
933
|
+
* So `redact` is an **honest, typed `CLI_NOT_IMPLEMENTED`** (never a fake success, never a partial
|
|
934
|
+
* scrub presented as de-identified, never invent a capability the ground layer lacks),
|
|
935
|
+
* gated on `@cosyte/deid`, with this seam as its one drop-in point. When `@cosyte/deid` lands and is
|
|
936
|
+
* conformance-graded, {@link deidStatus} flips to `available` and the command reads the input, calls
|
|
937
|
+
* the delegated de-identifier, and emits the de-identified model: the command surface (`redact`,
|
|
938
|
+
* `deid`, `<file|->`, `--format`) already exists so that change is additive.
|
|
939
|
+
*
|
|
940
|
+
* @packageDocumentation
|
|
941
|
+
*/
|
|
942
|
+
/** The value-free reason `redact`/`deid` reports while the ground-layer library is unavailable. */
|
|
943
|
+
declare const DEID_UNAVAILABLE_REASON: string;
|
|
944
|
+
/**
|
|
945
|
+
* Whether de-identification is available, and (while it is not) the value-free reason why. The one
|
|
946
|
+
* function the `redact` command consults **before reading any input**, so a `redact` invocation never
|
|
947
|
+
* touches the PHI it cannot yet safely strip.
|
|
948
|
+
*/
|
|
949
|
+
interface DeidAvailability {
|
|
950
|
+
/** `true` once `@cosyte/deid` is wired; `false` today. */
|
|
951
|
+
readonly available: boolean;
|
|
952
|
+
/** A value-free explanation shown when {@link DeidAvailability.available} is `false`. */
|
|
953
|
+
readonly reason: string;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Report the current de-identification availability. Today it is always unavailable (see the module
|
|
957
|
+
* docs); this is the single line that flips when `@cosyte/deid` is wired.
|
|
958
|
+
*
|
|
959
|
+
* @returns The {@link DeidAvailability}: `{ available: false, reason }` until `@cosyte/deid` ships.
|
|
960
|
+
* @example
|
|
961
|
+
* ```ts
|
|
962
|
+
* import { deidStatus } from "@cosyte/cli";
|
|
963
|
+
*
|
|
964
|
+
* deidStatus().available; // => false
|
|
965
|
+
* ```
|
|
966
|
+
*/
|
|
967
|
+
declare function deidStatus(): DeidAvailability;
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* The top-level **subcommand dispatcher**: a small hand-rolled router over Node's built-in
|
|
971
|
+
* `util.parseArgs`, deliberately **third-party-framework-free** so the CLI core keeps zero third-party
|
|
972
|
+
* runtime dependencies. It maps `argv[0]` to a command handler, serves `--help`
|
|
973
|
+
* and `--version`, and turns any unexpected exception into a value-free `CLI_INTERNAL` result so a
|
|
974
|
+
* stack trace carrying input can never reach the terminal.
|
|
975
|
+
*
|
|
976
|
+
* @packageDocumentation
|
|
977
|
+
*/
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Run a full `cosyte` invocation as data.
|
|
981
|
+
*
|
|
982
|
+
* @param argv - The arguments after the program name (i.e. `process.argv.slice(2)`).
|
|
983
|
+
* @param deps - Injected input readers ({@link RunDeps}).
|
|
984
|
+
* @returns The {@link RunResult} to write to the process streams and exit with.
|
|
985
|
+
* @example
|
|
986
|
+
* ```ts
|
|
987
|
+
* import { run } from "@cosyte/cli";
|
|
988
|
+
*
|
|
989
|
+
* const deps = { readFile: async () => new Uint8Array(), readStdin: async () => new Uint8Array() };
|
|
990
|
+
* const { exit } = await run(["--version"], deps);
|
|
991
|
+
* exit; // => 0
|
|
992
|
+
* ```
|
|
993
|
+
*/
|
|
994
|
+
declare function run(argv: string[], deps: RunDeps): Promise<RunResult>;
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* `cosyte parse <file|-> [--format …] [--ndjson] [--json] [--quiet] [--no-color]`
|
|
998
|
+
*
|
|
999
|
+
* Read a file (or stdin via `-`), **autodetect the format by content** (or honour `--format`), route to
|
|
1000
|
+
* the wrapped parser via the lazy per-format registry (`core/parsers.ts`) (so `cosyte parse msg.hl7`
|
|
1001
|
+
* never loads the DICOM or X12 code) and emit the parsed model as **typed JSON on stdout**. Every
|
|
1002
|
+
* failure is a value-free diagnostic on stderr with a stable `CLI_*` code and the documented exit code.
|
|
1003
|
+
*
|
|
1004
|
+
* **Multi-message / streaming.** A single message emits one pretty (or compact under `--json`)
|
|
1005
|
+
* JSON envelope, exactly as before. A **multi-record** input emits **NDJSON** (one compact envelope
|
|
1006
|
+
* per line) with per-record isolation: a record that fails to parse becomes a value-free
|
|
1007
|
+
* `{ record, error }` line and the stream continues, and the overall exit is a data error (`65`) if any
|
|
1008
|
+
* record failed. Two inputs are multi-record: an **MLLP** stream (each VT-framed frame is an enclosed
|
|
1009
|
+
* HL7 message) and any input under **`--ndjson`** (each non-empty line is a record: the FHIR bulk-data
|
|
1010
|
+
* convention).
|
|
1011
|
+
*
|
|
1012
|
+
* The CLI adds **no** parsing of its own: it routes, reads, and shapes output; `cosyte parse` equals the
|
|
1013
|
+
* wrapped library's programmatic parse.
|
|
1014
|
+
*
|
|
1015
|
+
* @packageDocumentation
|
|
1016
|
+
*/
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* Run the `parse` command.
|
|
1020
|
+
*
|
|
1021
|
+
* @param args - The arguments after the `parse` subcommand token.
|
|
1022
|
+
* @param deps - Injected input readers ({@link RunDeps}).
|
|
1023
|
+
* @param posture - The resolved {@link PhiPosture}. Defaults to {@link VALUE_FREE}; under
|
|
1024
|
+
* `--unsafe-show-values` a bounded excerpt of the offending input is appended to a `CLI_PARSE_FAILED`
|
|
1025
|
+
* diagnostic (the single, opt-in value-echoing surface): single-record mode only.
|
|
1026
|
+
* @returns A {@link RunResult}: the typed-JSON model (or NDJSON records) on `stdout`, a value-free note
|
|
1027
|
+
* (or nothing) on `stderr`, and the resolved exit code. Never throws a {@link CliError}: it resolves
|
|
1028
|
+
* it to a result; unexpected exceptions are caught by the dispatcher and mapped to `CLI_INTERNAL`.
|
|
1029
|
+
* @throws Never {@link CliError}; may propagate a truly unexpected error for the dispatcher to map.
|
|
1030
|
+
* @example
|
|
1031
|
+
* ```ts
|
|
1032
|
+
* import { parseCommand } from "@cosyte/cli";
|
|
1033
|
+
*
|
|
1034
|
+
* const enc = new TextEncoder();
|
|
1035
|
+
* const deps = {
|
|
1036
|
+
* readFile: async () => enc.encode('{"resourceType":"Patient","id":"x"}'),
|
|
1037
|
+
* readStdin: async () => new Uint8Array(),
|
|
1038
|
+
* };
|
|
1039
|
+
* (await parseCommand(["patient.json"], deps)).exit; // => 0
|
|
1040
|
+
* ```
|
|
1041
|
+
*/
|
|
1042
|
+
declare function parseCommand(args: string[], deps: RunDeps, posture?: PhiPosture): Promise<RunResult>;
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* `cosyte validate <file|-> [--format …] [--json] [--quiet] [--no-color]`
|
|
1046
|
+
*
|
|
1047
|
+
* Parse the input, run the **wrapped parser's own validation/conformance surface**, and let the
|
|
1048
|
+
* **exit code carry the verdict** so `cosyte validate` is usable as a CI gate: **`0` valid, `1`
|
|
1049
|
+
* invalid** (parseable but non-conformant), `65` unparseable, `66` no input, `2` usage. The
|
|
1050
|
+
* load-bearing rule: the CLI never prints a reassuring line and exits `0` on an invalid message.
|
|
1051
|
+
*
|
|
1052
|
+
* Findings are **value-free**: a stable code, a severity, and a positional locator (a FHIRPath, or an
|
|
1053
|
+
* HL7 segment/field index), never a field value. By default they render on **stderr**; `--json`
|
|
1054
|
+
* emits the same value-free verdict + findings as machine JSON on stdout. The CLI invents **no**
|
|
1055
|
+
* verdict of its own: FHIR validity is `@cosyte/fhir`'s `validateResource().valid` (plus any
|
|
1056
|
+
* error-severity read issue); HL7 validity is "parseable": its parser is Postel's-Law lenient and its
|
|
1057
|
+
* warnings are, by the library's design, non-fatal deviations (surfaced, never failing).
|
|
1058
|
+
*
|
|
1059
|
+
* @packageDocumentation
|
|
1060
|
+
*/
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* Run the `validate` command.
|
|
1064
|
+
*
|
|
1065
|
+
* @param args - The arguments after the `validate` subcommand token.
|
|
1066
|
+
* @param deps - Injected input readers ({@link RunDeps}).
|
|
1067
|
+
* @param posture - The resolved {@link PhiPosture} (governs only the opt-in unsafe excerpt on a
|
|
1068
|
+
* parse-failure diagnostic; a validation verdict is always value-free).
|
|
1069
|
+
* @returns A {@link RunResult} whose **exit code carries the verdict** (`0` valid / `1` invalid /
|
|
1070
|
+
* `65` unparseable / `66` no input / `2` usage). Value-free findings on `stderr`, or value-free
|
|
1071
|
+
* JSON on stdout under `--json`.
|
|
1072
|
+
* @throws Never {@link CliError}; may propagate a truly unexpected error for the dispatcher to map.
|
|
1073
|
+
* @example
|
|
1074
|
+
* ```ts
|
|
1075
|
+
* import { validateCommand } from "@cosyte/cli";
|
|
1076
|
+
*
|
|
1077
|
+
* const deps = {
|
|
1078
|
+
* readFile: async () => new TextEncoder().encode('{"resourceType":"Patient","gender":"male"}'),
|
|
1079
|
+
* readStdin: async () => new Uint8Array(),
|
|
1080
|
+
* };
|
|
1081
|
+
* (await validateCommand(["patient.json"], deps)).exit; // => 0
|
|
1082
|
+
* ```
|
|
1083
|
+
*/
|
|
1084
|
+
declare function validateCommand(args: string[], deps: RunDeps, posture?: PhiPosture): Promise<RunResult>;
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* `cosyte inspect <file|-> [--format …] [--json] [--quiet] [--no-color]`
|
|
1088
|
+
*
|
|
1089
|
+
* A human-readable, **value-free-by-default** structural summary of a message: its type, structural
|
|
1090
|
+
* counts, and a warning count: the "what shape is this?" answer, without ever printing a field
|
|
1091
|
+
* value. Unlike `parse`, whose stdout is the parsed model (the data channel), `inspect`'s
|
|
1092
|
+
* stdout is a *structural* summary composed only of counts and structural type **codes** (an HL7 message
|
|
1093
|
+
* type like `ADT^A01`, a FHIR `resourceType` like `Patient`, an X12 transaction-set id like `834`, a
|
|
1094
|
+
* DICOM SOP Class UID, an NCPDP `NewRx`): classification, never PHI. `--json` emits the same value-free
|
|
1095
|
+
* summary as machine JSON. The per-format summaries are built by the shared registry (`core/parsers.ts`).
|
|
1096
|
+
*
|
|
1097
|
+
* @packageDocumentation
|
|
1098
|
+
*/
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* Run the `inspect` command.
|
|
1102
|
+
*
|
|
1103
|
+
* @param args - The arguments after the `inspect` subcommand token.
|
|
1104
|
+
* @param deps - Injected input readers ({@link RunDeps}).
|
|
1105
|
+
* @param posture - The resolved {@link PhiPosture} (governs only the opt-in unsafe excerpt on a
|
|
1106
|
+
* parse-failure diagnostic; the summary itself is always value-free).
|
|
1107
|
+
* @returns A {@link RunResult}: the value-free structural summary on `stdout` (human, or JSON under
|
|
1108
|
+
* `--json`), exit `0`; a parse failure is a data error (`65`), an unreadable file `66`, a bad flag `2`.
|
|
1109
|
+
* @throws Never {@link CliError}; may propagate a truly unexpected error for the dispatcher to map.
|
|
1110
|
+
* @example
|
|
1111
|
+
* ```ts
|
|
1112
|
+
* import { inspectCommand } from "@cosyte/cli";
|
|
1113
|
+
*
|
|
1114
|
+
* const deps = {
|
|
1115
|
+
* readFile: async () => new TextEncoder().encode('{"resourceType":"Patient"}'),
|
|
1116
|
+
* readStdin: async () => new Uint8Array(),
|
|
1117
|
+
* };
|
|
1118
|
+
* (await inspectCommand(["patient.json", "--json"], deps)).exit; // => 0
|
|
1119
|
+
* ```
|
|
1120
|
+
*/
|
|
1121
|
+
declare function inspectCommand(args: string[], deps: RunDeps, posture?: PhiPosture): Promise<RunResult>;
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* `cosyte fmt <file|-> [--format …] [--quiet] [--no-color]`
|
|
1125
|
+
*
|
|
1126
|
+
* **Canonical re-serialization.** Parse the input and emit it back through the wrapped library's
|
|
1127
|
+
* **conservative, spec-clean serializer**: HL7 via `Hl7Message.toString()` (CR-separated, spec-clean
|
|
1128
|
+
* HL7), FHIR via `serializeResource` (canonical JSON, decimals byte-exact). The CLI **never
|
|
1129
|
+
* re-canonicalizes on its own**: the output is exactly the wrapped serializer's, so
|
|
1130
|
+
* `fmt` round-trips a spec-clean input through the parser.
|
|
1131
|
+
*
|
|
1132
|
+
* `fmt`'s stdout **is** the data channel: a re-serialization of the message, values included, going to
|
|
1133
|
+
* the sink the user chose. Every *secondary* surface stays value-free: a warning-count note on stderr
|
|
1134
|
+
* (suppressible with `--quiet`), and an unparseable input is a data error (`65`) with **no partial
|
|
1135
|
+
* emit**, never a half-serialized message.
|
|
1136
|
+
*
|
|
1137
|
+
* @packageDocumentation
|
|
1138
|
+
*/
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Run the `fmt` command.
|
|
1142
|
+
*
|
|
1143
|
+
* @param args - The arguments after the `fmt` subcommand token.
|
|
1144
|
+
* @param deps - Injected input readers ({@link RunDeps}).
|
|
1145
|
+
* @param posture - The resolved {@link PhiPosture} (governs only the opt-in unsafe excerpt on a
|
|
1146
|
+
* parse-failure diagnostic).
|
|
1147
|
+
* @returns A {@link RunResult}: the canonical re-serialization on `stdout` (exit `0`), a value-free
|
|
1148
|
+
* warning-count note on `stderr` unless `--quiet`; an unparseable input is a data error (`65`) with
|
|
1149
|
+
* no partial emit, an unreadable file `66`, a bad flag `2`.
|
|
1150
|
+
* @throws Never {@link CliError}; may propagate a truly unexpected error for the dispatcher to map.
|
|
1151
|
+
* @example
|
|
1152
|
+
* ```ts
|
|
1153
|
+
* import { fmtCommand } from "@cosyte/cli";
|
|
1154
|
+
*
|
|
1155
|
+
* const deps = {
|
|
1156
|
+
* readFile: async () => new TextEncoder().encode('{ "resourceType":"Patient" , "id":"x" }'),
|
|
1157
|
+
* readStdin: async () => new Uint8Array(),
|
|
1158
|
+
* };
|
|
1159
|
+
* (await fmtCommand(["patient.json"], deps)).exit; // => 0
|
|
1160
|
+
* ```
|
|
1161
|
+
*/
|
|
1162
|
+
declare function fmtCommand(args: string[], deps: RunDeps, posture?: PhiPosture): Promise<RunResult>;
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* `cosyte convert <file|-> --to fhir [--json] [--quiet] [--no-color]`
|
|
1166
|
+
*
|
|
1167
|
+
* Convert an **HL7 v2** message to **FHIR R4** via **`@cosyte/transform`**: the
|
|
1168
|
+
* consumer-of-consumers command. The CLI adds **no** mapping logic of its own: it parses the input
|
|
1169
|
+
* with `@cosyte/hl7`, hands the parsed message to `transform`'s `toFhir`, and serializes the resulting
|
|
1170
|
+
* FHIR `Bundle` with `@cosyte/fhir`. The mapping guarantees are the library's, graded by *its* gate;
|
|
1171
|
+
* `cosyte convert` equals `transform`'s programmatic output.
|
|
1172
|
+
*
|
|
1173
|
+
* The converted FHIR `Bundle` is the user's explicit request, so it goes to **stdout** (the data
|
|
1174
|
+
* channel). Every conversion problem is one of `transform`'s value-free {@link TransformIssue}s: a
|
|
1175
|
+
* stable code, a severity, and a positional locator (a v2 index and an optional FHIRPath), **never** a
|
|
1176
|
+
* field value: surfaced on stderr (or as value-free JSON under `--json`). The load-bearing rule
|
|
1177
|
+
* (mirroring `validate`): a conversion that produces an **error-severity** issue exits **`1`**, never
|
|
1178
|
+
* `0`: the tool worked, but the conversion has a real problem a CI gate must see.
|
|
1179
|
+
*
|
|
1180
|
+
* `--to fhir` is required and is the only supported target (HL7 v2 → FHIR R4). The source must be
|
|
1181
|
+
* HL7 v2: a recognised-but-non-HL7 input (e.g. a FHIR document) is a value-free
|
|
1182
|
+
* `CLI_FORMAT_UNSUPPORTED` data error (`65`), never a fake conversion.
|
|
1183
|
+
*
|
|
1184
|
+
* @packageDocumentation
|
|
1185
|
+
*/
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* Run the `convert` command.
|
|
1189
|
+
*
|
|
1190
|
+
* @param args - The arguments after the `convert` subcommand token.
|
|
1191
|
+
* @param deps - Injected input readers ({@link RunDeps}).
|
|
1192
|
+
* @param posture - The resolved {@link PhiPosture} (governs only the opt-in unsafe excerpt on an HL7
|
|
1193
|
+
* parse-failure diagnostic; the converted bundle and the value-free findings are unaffected).
|
|
1194
|
+
* @returns A {@link RunResult}: the converted FHIR `Bundle` on `stdout` (the data channel), value-free
|
|
1195
|
+
* findings on `stderr` (or JSON under `--json`), and an exit code that carries the outcome: `0`
|
|
1196
|
+
* clean · `1` an error-severity conversion issue · `65` the HL7 input could not be parsed or is not
|
|
1197
|
+
* an HL7 v2 source · `66` no input · `2` usage.
|
|
1198
|
+
* @throws Never {@link CliError}; may propagate a truly unexpected error for the dispatcher to map to
|
|
1199
|
+
* `CLI_INTERNAL`.
|
|
1200
|
+
* @example
|
|
1201
|
+
* ```ts
|
|
1202
|
+
* import { convertCommand } from "@cosyte/cli";
|
|
1203
|
+
*
|
|
1204
|
+
* const deps = {
|
|
1205
|
+
* readFile: async () => new TextEncoder().encode("MSH|^~\\&|A|B|C|D|20240101||ADT^A01|1|P|2.5\r"),
|
|
1206
|
+
* readStdin: async () => new Uint8Array(),
|
|
1207
|
+
* };
|
|
1208
|
+
* (await convertCommand(["adt.hl7", "--to", "fhir"], deps)).exit; // => 0
|
|
1209
|
+
* ```
|
|
1210
|
+
*/
|
|
1211
|
+
declare function convertCommand(args: string[], deps: RunDeps, posture?: PhiPosture): Promise<RunResult>;
|
|
1212
|
+
/**
|
|
1213
|
+
* Decide the conversion outcome from the library's value-free findings: whether any is
|
|
1214
|
+
* **error-severity** (which drives a non-zero exit: the load-bearing "a conversion error is never
|
|
1215
|
+
* exit 0" rule) and the value-free human report. The severity classification is
|
|
1216
|
+
* the library's: `@cosyte/transform` fixes each issue's severity; the CLI only reads it. Exported so
|
|
1217
|
+
* the exit-verdict and report logic is unit-testable with synthetic findings, independent of which HL7
|
|
1218
|
+
* message happens to produce an error.
|
|
1219
|
+
*
|
|
1220
|
+
* @param findings - The value-free {@link Finding}s the conversion produced.
|
|
1221
|
+
* @returns `hasError` (true iff any finding is error/fatal severity) and the rendered value-free
|
|
1222
|
+
* `report` (one line per finding + a summary line).
|
|
1223
|
+
* @example
|
|
1224
|
+
* ```ts
|
|
1225
|
+
* import { convertOutcome } from "@cosyte/cli";
|
|
1226
|
+
*
|
|
1227
|
+
* convertOutcome([{ code: "TRANSFORM_RESOURCE_INVALID", severity: "error", location: "PID" }])
|
|
1228
|
+
* .hasError; // => true
|
|
1229
|
+
* convertOutcome([{ code: "TRANSFORM_ELEMENT_DROPPED", severity: "information", location: "PID.13" }])
|
|
1230
|
+
* .hasError; // => false
|
|
1231
|
+
* ```
|
|
1232
|
+
*/
|
|
1233
|
+
declare function convertOutcome(findings: readonly Finding[]): {
|
|
1234
|
+
readonly hasError: boolean;
|
|
1235
|
+
readonly report: string;
|
|
1236
|
+
};
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* `cosyte map-codes <conceptmap|-> --code <code> [--system <uri>] [--version <v>] [--display <d>]
|
|
1240
|
+
* [--json] [--quiet]`
|
|
1241
|
+
*
|
|
1242
|
+
* Translate a single source coding through a **BYO FHIR R4 ConceptMap** via
|
|
1243
|
+
* **`@cosyte/terminology`**. The positional argument is the ConceptMap document (a file, or `-` for
|
|
1244
|
+
* stdin); `--system` + `--code` (and optional `--version`/`--display`) name the source coding to
|
|
1245
|
+
* translate. The CLI adds **no** mapping content of its own: `terminology` is content-free and
|
|
1246
|
+
* never fabricates a target; the CLI loads the user's map and forwards the translation faithfully.
|
|
1247
|
+
*
|
|
1248
|
+
* A ConceptMap and a terminology code are **reference data, not PHI**, so the translation result
|
|
1249
|
+
* (the matched target coding(s), or the value-free "unmapped" signal) is the user's explicit request
|
|
1250
|
+
* and goes to **stdout**. The exit code carries the outcome so it is usable as a gate: **`0`** at
|
|
1251
|
+
* least one match · **`1`** no mapping found (the never-fabricate `TERM_TRANSLATE_UNMAPPED` outcome) ·
|
|
1252
|
+
* `65` the supplied ConceptMap is not valid JSON / not a loadable ConceptMap · `66` no input · `2`
|
|
1253
|
+
* usage. stderr stays value-free (stable codes and counts only).
|
|
1254
|
+
*
|
|
1255
|
+
* @packageDocumentation
|
|
1256
|
+
*/
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
* Run the `map-codes` command.
|
|
1260
|
+
*
|
|
1261
|
+
* @param args - The arguments after the `map-codes` subcommand token.
|
|
1262
|
+
* @param deps - Injected input readers ({@link RunDeps}); the positional argument (or `-`) is the BYO
|
|
1263
|
+
* ConceptMap document.
|
|
1264
|
+
* @returns A {@link RunResult}: the translation result on `stdout` (matched target coding(s) or the
|
|
1265
|
+
* value-free unmapped signal), a value-free note on `stderr` (unless `--quiet`), and an exit code
|
|
1266
|
+
* carrying the outcome: `0` mapped · `1` unmapped · `65` unloadable ConceptMap · `66` no input ·
|
|
1267
|
+
* `2` usage.
|
|
1268
|
+
* @throws Never {@link CliError}; may propagate a truly unexpected error for the dispatcher to map to
|
|
1269
|
+
* `CLI_INTERNAL`.
|
|
1270
|
+
* @example
|
|
1271
|
+
* ```ts
|
|
1272
|
+
* import { mapCodesCommand } from "@cosyte/cli";
|
|
1273
|
+
*
|
|
1274
|
+
* const cm = JSON.stringify({
|
|
1275
|
+
* resourceType: "ConceptMap",
|
|
1276
|
+
* group: [
|
|
1277
|
+
* {
|
|
1278
|
+
* source: "http://hl7.org/fhir/administrative-gender",
|
|
1279
|
+
* target: "http://terminology.hl7.org/CodeSystem/v2-0001",
|
|
1280
|
+
* element: [{ code: "male", target: [{ code: "M", equivalence: "equivalent" }] }],
|
|
1281
|
+
* },
|
|
1282
|
+
* ],
|
|
1283
|
+
* });
|
|
1284
|
+
* const deps = {
|
|
1285
|
+
* readFile: async () => new TextEncoder().encode(cm),
|
|
1286
|
+
* readStdin: async () => new Uint8Array(),
|
|
1287
|
+
* };
|
|
1288
|
+
* const r = await mapCodesCommand(
|
|
1289
|
+
* ["gender.json", "--system", "http://hl7.org/fhir/administrative-gender", "--code", "male"],
|
|
1290
|
+
* deps,
|
|
1291
|
+
* );
|
|
1292
|
+
* r.exit; // => 0
|
|
1293
|
+
* ```
|
|
1294
|
+
*/
|
|
1295
|
+
declare function mapCodesCommand(args: string[], deps: RunDeps): Promise<RunResult>;
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* `cosyte redact <file|-> [--format …]` (alias `cosyte deid …`): the command whose *job* is to strip
|
|
1299
|
+
* PHI, emitting a de-identified copy of the parsed model.
|
|
1300
|
+
*
|
|
1301
|
+
* **It is deliberately not implemented yet.** De-identification belongs to **`@cosyte/deid`**, which
|
|
1302
|
+
* is unpublished, and the wrapped parsers expose no de-id API to delegate to. A
|
|
1303
|
+
* built-in "minimal Safe-Harbor" pass over only the obvious PHI loci would leave PHI behind and
|
|
1304
|
+
* present a **false-safety impression**: the cardinal hazard `redact` exists to avoid. So this
|
|
1305
|
+
* command is an **honest, typed `CLI_NOT_IMPLEMENTED`** (never a fake success, never a partial scrub
|
|
1306
|
+
* dressed up as de-identified), gated on the de-identification seam in `core/deid.ts`.
|
|
1307
|
+
*
|
|
1308
|
+
* To keep that guarantee airtight, the command **never reads the input**: it consults
|
|
1309
|
+
* {@link deidStatus} first, and (while de-id is unavailable) reports the value-free reason and exits
|
|
1310
|
+
* `EX_UNAVAILABLE` (`69`) without ever touching the file's bytes. The command surface (positional
|
|
1311
|
+
* `<file|->`, `--format`) already exists so wiring `@cosyte/deid` later is additive.
|
|
1312
|
+
*
|
|
1313
|
+
* @packageDocumentation
|
|
1314
|
+
*/
|
|
1315
|
+
|
|
1316
|
+
/**
|
|
1317
|
+
* Run the `redact` / `deid` command.
|
|
1318
|
+
*
|
|
1319
|
+
* @param args - The arguments after the `redact`/`deid` subcommand token.
|
|
1320
|
+
* @returns A {@link RunResult}. While `@cosyte/deid` is unavailable: empty `stdout`, a value-free
|
|
1321
|
+
* `CLI_NOT_IMPLEMENTED` diagnostic on `stderr`, and exit `EX_UNAVAILABLE` (`69`). A malformed
|
|
1322
|
+
* invocation (an unknown flag) is a usage error (exit `2`). The input is **never read**.
|
|
1323
|
+
* @example
|
|
1324
|
+
* ```ts
|
|
1325
|
+
* import { redactCommand } from "@cosyte/cli";
|
|
1326
|
+
*
|
|
1327
|
+
* const r = redactCommand(["message.hl7"]);
|
|
1328
|
+
* r.exit; // => 69
|
|
1329
|
+
* ```
|
|
1330
|
+
*/
|
|
1331
|
+
declare function redactCommand(args: string[]): RunResult;
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* `cosyte completion <bash|zsh|fish>`
|
|
1335
|
+
*
|
|
1336
|
+
* Print a shell completion script for `cosyte`, generated from the command tree, to **stdout**: the
|
|
1337
|
+
* user pipes it into their shell (`source <(cosyte completion bash)`) or saves it to their completions
|
|
1338
|
+
* directory. The script is static and value-free: it names commands, subcommand tokens, and flags,
|
|
1339
|
+
* never any input. An unknown or missing shell is a usage error (exit `2`), never a silent no-op.
|
|
1340
|
+
*
|
|
1341
|
+
* @packageDocumentation
|
|
1342
|
+
*/
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* Run the `completion` command.
|
|
1346
|
+
*
|
|
1347
|
+
* @param args - The arguments after the `completion` subcommand token; the first is the shell name.
|
|
1348
|
+
* @returns A {@link RunResult}: the completion script on `stdout` (exit `0`), or a value-free usage
|
|
1349
|
+
* error (exit `2`) when the shell is missing or unrecognised.
|
|
1350
|
+
* @example
|
|
1351
|
+
* ```ts
|
|
1352
|
+
* import { completionCommand } from "@cosyte/cli";
|
|
1353
|
+
*
|
|
1354
|
+
* completionCommand(["bash"]).exit; // => 0
|
|
1355
|
+
* completionCommand(["powershell"]).exit; // => 2
|
|
1356
|
+
* ```
|
|
1357
|
+
*/
|
|
1358
|
+
declare function completionCommand(args: string[]): RunResult;
|
|
1359
|
+
|
|
1360
|
+
export { CLI_CODES, type CliCode, CliError, type CosyteFormat, DEID_UNAVAILABLE_REASON, DETECTABLE_FORMATS, type DeidAvailability, type DetectResult, EXIT, type ExitCode, type Finding, type FmtResult, type InputResolution, type InspectSummary, KNOWN_FORMATS, OP_SUPPORT, type Op, type ParseResult, type ParseWarning, type PhiPosture, type ResolvedInput, RunDeps, type RunResult, SHOW_VALUES, UNSAFE_EXCERPT_MAX, UNSAFE_SHOW_VALUES_FLAG, VALUE_FREE, VERSION, type Verdict, asCosyteFormat, classifyCandidates, completionCommand, convertCommand, convertOutcome, deframeMllp, deidStatus, detectFormat, detectionError, errorResult, extractPhiPosture, extractStableCode, fmtCommand, fmtFormat, formatDiagnostic, formatHl7Position, formatsSupporting, inspectCommand, inspectFormat, loadOptional, mapCodesCommand, parseCommand, parseFailureResult, parseFormat, redactCommand, resolveInput, run, supportsOp, toCliError, unsafeInputSuffix, validateCommand, validateFormat, valueFreeLocator };
|