@rethinkhealth/hl7v2-ack 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +137 -0
- package/dist/acknowledge.d.ts +20 -0
- package/dist/acknowledge.d.ts.map +1 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/uid.d.ts +6 -0
- package/dist/uid.d.ts.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Rethink Health
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# @rethinkhealth/hl7v2-ack
|
|
2
|
+
|
|
3
|
+
HL7v2 acknowledgment message builder and typed error classes — builds spec-compliant ACK/NAK response ASTs from parsed HL7v2 messages.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides:
|
|
8
|
+
|
|
9
|
+
1. **`acknowledge()`** — Builds a complete ACK/NAK `Root` AST from an original message's AST
|
|
10
|
+
2. **Error classes** — `AckError` (AE) and `AckReject` (AR) with HL7v2 error codes and severity
|
|
11
|
+
3. **`uid()`** — Generates unique MSH-10 control IDs via `nanoid`
|
|
12
|
+
|
|
13
|
+
**Key characteristics:**
|
|
14
|
+
|
|
15
|
+
- **AST in, AST out** — works with `@rethinkhealth/hl7v2-ast` trees, not raw strings
|
|
16
|
+
- **Spec-compliant** — produces MSH, MSA, and optional ERR segments per HL7v2 standard
|
|
17
|
+
- **Composable** — use standalone or with `@rethinkhealth/hl7v2-mllp-ack` middleware
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @rethinkhealth/hl7v2-ack
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Success (AA)
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { acknowledge } from "@rethinkhealth/hl7v2-ack";
|
|
31
|
+
import { toHl7v2 } from "@rethinkhealth/hl7v2-to-hl7v2";
|
|
32
|
+
|
|
33
|
+
const ack = acknowledge(originalTree);
|
|
34
|
+
const raw = toHl7v2(ack);
|
|
35
|
+
// MSH|^~\&|RecvApp|RecvFac|SendApp|SendFac|20240115023000||ACK^A01|<auto-id>|P|2.5.1
|
|
36
|
+
// MSA|AA|MSG001
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The ACK sender (MSH-3/MSH-4) is derived from the original message's MSH-5/MSH-6 by default.
|
|
40
|
+
|
|
41
|
+
### Error (AE)
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { acknowledge, AckError } from "@rethinkhealth/hl7v2-ack";
|
|
45
|
+
|
|
46
|
+
const error = new AckError("Validation failed", {
|
|
47
|
+
errorCode: "207",
|
|
48
|
+
severity: "E",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const ack = acknowledge(originalTree, { error });
|
|
52
|
+
// MSA|AE|MSG001|Validation failed
|
|
53
|
+
// ERR|||207|E
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Reject (AR)
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { acknowledge, AckReject } from "@rethinkhealth/hl7v2-ack";
|
|
60
|
+
|
|
61
|
+
const error = new AckReject("Unsupported message type", {
|
|
62
|
+
errorCode: "200",
|
|
63
|
+
severity: "E",
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const ack = acknowledge(originalTree, { error });
|
|
67
|
+
// MSA|AR|MSG001|Unsupported message type
|
|
68
|
+
// ERR|||200|E
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
### `acknowledge(origin, options?)`
|
|
74
|
+
|
|
75
|
+
Builds an ACK/NAK `Root` AST from the original message tree.
|
|
76
|
+
|
|
77
|
+
| Parameter | Type | Description |
|
|
78
|
+
| --------------------------- | -------------- | ------------------------------------------------------------------ |
|
|
79
|
+
| `origin` | `Root` | Parsed AST of the original HL7v2 message |
|
|
80
|
+
| `options.id` | `string` | Custom MSH-10 control ID. Auto-generated via `uid()` when omitted |
|
|
81
|
+
| `options.sending` | `SendingInfo` | MSH-3/MSH-4 of the ACK. Defaults to original message's MSH-5/MSH-6 |
|
|
82
|
+
| `options.processingId` | `string` | MSH-11 processing ID. Defaults to original message's MSH-11 |
|
|
83
|
+
| `options.error` | `AckException` | Sets AE/AR code and populates MSA-3 with the error message |
|
|
84
|
+
| `options.includeErrSegment` | `boolean` | Include ERR segment when error is provided. Defaults to `true` |
|
|
85
|
+
|
|
86
|
+
Returns a `Root` node containing MSH, MSA, and optionally ERR segments.
|
|
87
|
+
|
|
88
|
+
### `AckError`
|
|
89
|
+
|
|
90
|
+
Error class for application errors (ACK code `AE`).
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
new AckError(message, { errorCode, severity?, cause? })
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `AckReject`
|
|
97
|
+
|
|
98
|
+
Error class for application rejects (ACK code `AR`).
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
new AckReject(message, { errorCode, severity?, cause? })
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Both extend `AckException`, which extends `Error`. The `cause` option supports error chain debugging via standard `ErrorOptions`.
|
|
105
|
+
|
|
106
|
+
### `uid(options?)`
|
|
107
|
+
|
|
108
|
+
Generates a unique ID suitable for MSH-10 control IDs.
|
|
109
|
+
|
|
110
|
+
| Parameter | Type | Default | Description |
|
|
111
|
+
| ---------------- | -------- | ------- | -------------------------------- |
|
|
112
|
+
| `options.prefix` | `string` | — | Optional prefix for the ID |
|
|
113
|
+
| `options.size` | `number` | `20` | Total length of the generated ID |
|
|
114
|
+
|
|
115
|
+
## Contributing
|
|
116
|
+
|
|
117
|
+
We welcome contributions! Please see our [Contributing Guide][github-contributing] for more details.
|
|
118
|
+
|
|
119
|
+
1. Fork the repository
|
|
120
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
121
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
122
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
123
|
+
5. Open a Pull Request
|
|
124
|
+
|
|
125
|
+
## Code of Conduct
|
|
126
|
+
|
|
127
|
+
To ensure a welcoming and positive environment, we have a [Code of Conduct][github-code-of-conduct] that all contributors and participants are expected to adhere to.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
Copyright 2025 Rethink Health, SUARL. All rights reserved.
|
|
132
|
+
|
|
133
|
+
This program is licensed to you under the terms of the [MIT License](https://opensource.org/licenses/MIT). This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the [LICENSE][github-license] file for details.
|
|
134
|
+
|
|
135
|
+
[github-code-of-conduct]: https://github.com/rethinkhealth/hl7v2/blob/main/CODE_OF_CONDUCT.md
|
|
136
|
+
[github-license]: https://github.com/rethinkhealth/hl7v2/blob/main/LICENSE
|
|
137
|
+
[github-contributing]: https://github.com/rethinkhealth/hl7v2/blob/main/CONTRIBUTING.md
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Root } from "@rethinkhealth/hl7v2-ast";
|
|
2
|
+
import type { AckException } from "./errors";
|
|
3
|
+
export interface SendingInfo {
|
|
4
|
+
application: string;
|
|
5
|
+
facility: string;
|
|
6
|
+
}
|
|
7
|
+
export interface AcknowledgeOptions {
|
|
8
|
+
/** Custom MSH-10 control ID. Auto-generated via `uid()` when omitted. */
|
|
9
|
+
id?: string;
|
|
10
|
+
/** MSH-3/MSH-4 of the ACK. Defaults to the original message's MSH-5/MSH-6. */
|
|
11
|
+
sending?: SendingInfo;
|
|
12
|
+
/** MSH-11 processing ID. Defaults to the original message's MSH-11. */
|
|
13
|
+
processingId?: string;
|
|
14
|
+
/** When provided, sets the ACK code (AE/AR) and populates MSA-3 with the error message. */
|
|
15
|
+
error?: AckException;
|
|
16
|
+
/** Include ERR segment when an error is provided. Defaults to `true`. */
|
|
17
|
+
includeErrSegment?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function acknowledge(origin: Root, options?: AcknowledgeOptions): Root;
|
|
20
|
+
//# sourceMappingURL=acknowledge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acknowledge.d.ts","sourceRoot":"","sources":["../src/acknowledge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAW,MAAM,0BAA0B,CAAC;AAK9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2FAA2F;IAC3F,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAoED,wBAAgB,WAAW,CACzB,MAAM,EAAE,IAAI,EACZ,OAAO,GAAE,kBAAuB,GAC/B,IAAI,CAeN"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface AckExceptionOptions extends ErrorOptions {
|
|
2
|
+
errorCode: string;
|
|
3
|
+
severity?: string;
|
|
4
|
+
}
|
|
5
|
+
type AckCode = "AE" | "AR";
|
|
6
|
+
export declare abstract class AckException extends Error {
|
|
7
|
+
abstract readonly code: AckCode;
|
|
8
|
+
readonly errorCode: string;
|
|
9
|
+
readonly severity: string | undefined;
|
|
10
|
+
constructor(message: string, options: AckExceptionOptions);
|
|
11
|
+
}
|
|
12
|
+
export declare class AckError extends AckException {
|
|
13
|
+
readonly code: "AE";
|
|
14
|
+
constructor(message: string, options: AckExceptionOptions);
|
|
15
|
+
}
|
|
16
|
+
export declare class AckReject extends AckException {
|
|
17
|
+
readonly code: "AR";
|
|
18
|
+
constructor(message: string, options: AckExceptionOptions);
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3B,8BAAsB,YAAa,SAAQ,KAAK;IAC9C,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;gBAE1B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB;CAK1D;AAED,qBAAa,QAAS,SAAQ,YAAY;IACxC,QAAQ,CAAC,IAAI,EAAG,IAAI,CAAU;gBAElB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB;CAI1D;AAED,qBAAa,SAAU,SAAQ,YAAY;IACzC,QAAQ,CAAC,IAAI,EAAG,IAAI,CAAU;gBAElB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB;CAI1D"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { acknowledge } from "./acknowledge";
|
|
2
|
+
export type { AcknowledgeOptions, SendingInfo } from "./acknowledge";
|
|
3
|
+
export { AckError, AckException, AckReject } from "./errors";
|
|
4
|
+
export type { AckExceptionOptions } from "./errors";
|
|
5
|
+
export { uid } from "./uid";
|
|
6
|
+
export type { Options as UidOptions } from "./uid";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,YAAY,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,OAAO,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/acknowledge.ts
|
|
2
|
+
import { c, f, m, s } from "@rethinkhealth/hl7v2-builder";
|
|
3
|
+
import { value } from "@rethinkhealth/hl7v2-util-query";
|
|
4
|
+
import { Timestamp } from "@rethinkhealth/hl7v2-util-timestamp";
|
|
5
|
+
|
|
6
|
+
// src/uid.ts
|
|
7
|
+
import { nanoid } from "nanoid";
|
|
8
|
+
var MAX_LENGTH = 20;
|
|
9
|
+
function uid(options = {}) {
|
|
10
|
+
const { prefix, size = MAX_LENGTH } = options;
|
|
11
|
+
if (!prefix) {
|
|
12
|
+
return nanoid(size);
|
|
13
|
+
}
|
|
14
|
+
const remaining = size - prefix.length;
|
|
15
|
+
if (remaining <= 0) {
|
|
16
|
+
return prefix.slice(0, size);
|
|
17
|
+
}
|
|
18
|
+
return `${prefix}${nanoid(remaining)}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/acknowledge.ts
|
|
22
|
+
function extractOriginFields(tree) {
|
|
23
|
+
return {
|
|
24
|
+
controlId: value(tree, "MSH-10")?.value ?? "",
|
|
25
|
+
processingId: value(tree, "MSH-11")?.value ?? "P",
|
|
26
|
+
receivingApp: value(tree, "MSH-5")?.value ?? "",
|
|
27
|
+
receivingFac: value(tree, "MSH-6")?.value ?? "",
|
|
28
|
+
sendingApp: value(tree, "MSH-3")?.value ?? "",
|
|
29
|
+
sendingFac: value(tree, "MSH-4")?.value ?? "",
|
|
30
|
+
triggerEvent: value(tree, "MSH-9.2")?.value ?? "",
|
|
31
|
+
version: value(tree, "MSH-12")?.value ?? ""
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function buildMsh(origin, options) {
|
|
35
|
+
const sendApp = options.sending?.application ?? origin.receivingApp;
|
|
36
|
+
const sendFac = options.sending?.facility ?? origin.receivingFac;
|
|
37
|
+
const pid = options.processingId ?? origin.processingId;
|
|
38
|
+
const controlId = options.id ?? uid();
|
|
39
|
+
const msgType = origin.triggerEvent ? f(c("ACK"), c(origin.triggerEvent)) : f("ACK");
|
|
40
|
+
return s(
|
|
41
|
+
"MSH",
|
|
42
|
+
f("|"),
|
|
43
|
+
f("^~\\&"),
|
|
44
|
+
f(sendApp),
|
|
45
|
+
f(sendFac),
|
|
46
|
+
f(origin.sendingApp),
|
|
47
|
+
f(origin.sendingFac),
|
|
48
|
+
f(Timestamp.now().toString()),
|
|
49
|
+
f(""),
|
|
50
|
+
msgType,
|
|
51
|
+
f(controlId),
|
|
52
|
+
f(pid),
|
|
53
|
+
f(origin.version)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
function buildMsa(code, controlId, message) {
|
|
57
|
+
return message ? s("MSA", f(code), f(controlId), f(message)) : s("MSA", f(code), f(controlId));
|
|
58
|
+
}
|
|
59
|
+
function buildErr(error) {
|
|
60
|
+
return s("ERR", f(""), f(""), f(error.errorCode), f(error.severity ?? "E"));
|
|
61
|
+
}
|
|
62
|
+
function acknowledge(origin, options = {}) {
|
|
63
|
+
const { error, includeErrSegment = true } = options;
|
|
64
|
+
const fields = extractOriginFields(origin);
|
|
65
|
+
const code = error?.code ?? "AA";
|
|
66
|
+
const segments = [
|
|
67
|
+
buildMsh(fields, options),
|
|
68
|
+
buildMsa(code, fields.controlId, error?.message)
|
|
69
|
+
];
|
|
70
|
+
if (error && includeErrSegment) {
|
|
71
|
+
segments.push(buildErr(error));
|
|
72
|
+
}
|
|
73
|
+
return m(...segments);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/errors.ts
|
|
77
|
+
var AckException = class extends Error {
|
|
78
|
+
errorCode;
|
|
79
|
+
severity;
|
|
80
|
+
constructor(message, options) {
|
|
81
|
+
super(message, { cause: options.cause });
|
|
82
|
+
this.errorCode = options.errorCode;
|
|
83
|
+
this.severity = options.severity;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var AckError = class extends AckException {
|
|
87
|
+
code = "AE";
|
|
88
|
+
constructor(message, options) {
|
|
89
|
+
super(message, options);
|
|
90
|
+
this.name = "AckError";
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
var AckReject = class extends AckException {
|
|
94
|
+
code = "AR";
|
|
95
|
+
constructor(message, options) {
|
|
96
|
+
super(message, options);
|
|
97
|
+
this.name = "AckReject";
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
export {
|
|
101
|
+
AckError,
|
|
102
|
+
AckException,
|
|
103
|
+
AckReject,
|
|
104
|
+
acknowledge,
|
|
105
|
+
uid
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/acknowledge.ts","../src/uid.ts","../src/errors.ts"],"sourcesContent":["import type { Root, Segment } from \"@rethinkhealth/hl7v2-ast\";\nimport { c, f, m, s } from \"@rethinkhealth/hl7v2-builder\";\nimport { value } from \"@rethinkhealth/hl7v2-util-query\";\nimport { Timestamp } from \"@rethinkhealth/hl7v2-util-timestamp\";\n\nimport type { AckException } from \"./errors\";\nimport { uid } from \"./uid\";\n\nexport interface SendingInfo {\n application: string;\n facility: string;\n}\n\nexport interface AcknowledgeOptions {\n /** Custom MSH-10 control ID. Auto-generated via `uid()` when omitted. */\n id?: string;\n /** MSH-3/MSH-4 of the ACK. Defaults to the original message's MSH-5/MSH-6. */\n sending?: SendingInfo;\n /** MSH-11 processing ID. Defaults to the original message's MSH-11. */\n processingId?: string;\n /** When provided, sets the ACK code (AE/AR) and populates MSA-3 with the error message. */\n error?: AckException;\n /** Include ERR segment when an error is provided. Defaults to `true`. */\n includeErrSegment?: boolean;\n}\n\n// -- Field extraction ----\n\ninterface OriginFields {\n controlId: string;\n version: string;\n triggerEvent: string;\n processingId: string;\n sendingApp: string;\n sendingFac: string;\n receivingApp: string;\n receivingFac: string;\n}\n\nfunction extractOriginFields(tree: Root): OriginFields {\n return {\n controlId: value(tree, \"MSH-10\")?.value ?? \"\",\n processingId: value(tree, \"MSH-11\")?.value ?? \"P\",\n receivingApp: value(tree, \"MSH-5\")?.value ?? \"\",\n receivingFac: value(tree, \"MSH-6\")?.value ?? \"\",\n sendingApp: value(tree, \"MSH-3\")?.value ?? \"\",\n sendingFac: value(tree, \"MSH-4\")?.value ?? \"\",\n triggerEvent: value(tree, \"MSH-9.2\")?.value ?? \"\",\n version: value(tree, \"MSH-12\")?.value ?? \"\",\n };\n}\n\n// -- Segment builders ----\n\nfunction buildMsh(origin: OriginFields, options: AcknowledgeOptions): Segment {\n const sendApp = options.sending?.application ?? origin.receivingApp;\n const sendFac = options.sending?.facility ?? origin.receivingFac;\n const pid = options.processingId ?? origin.processingId;\n const controlId = options.id ?? uid();\n const msgType = origin.triggerEvent\n ? f(c(\"ACK\"), c(origin.triggerEvent))\n : f(\"ACK\");\n\n return s(\n \"MSH\",\n f(\"|\"),\n f(\"^~\\\\&\"),\n f(sendApp),\n f(sendFac),\n f(origin.sendingApp),\n f(origin.sendingFac),\n f(Timestamp.now().toString()),\n f(\"\"),\n msgType,\n f(controlId),\n f(pid),\n f(origin.version)\n );\n}\n\nfunction buildMsa(code: string, controlId: string, message?: string): Segment {\n return message\n ? s(\"MSA\", f(code), f(controlId), f(message))\n : s(\"MSA\", f(code), f(controlId));\n}\n\nfunction buildErr(error: AckException): Segment {\n return s(\"ERR\", f(\"\"), f(\"\"), f(error.errorCode), f(error.severity ?? \"E\"));\n}\n\n// -- Public API ----\n\nexport function acknowledge(\n origin: Root,\n options: AcknowledgeOptions = {}\n): Root {\n const { error, includeErrSegment = true } = options;\n const fields = extractOriginFields(origin);\n const code = error?.code ?? \"AA\";\n\n const segments: Segment[] = [\n buildMsh(fields, options),\n buildMsa(code, fields.controlId, error?.message),\n ];\n\n if (error && includeErrSegment) {\n segments.push(buildErr(error));\n }\n\n return m(...segments);\n}\n","import { nanoid } from \"nanoid\";\n\nconst MAX_LENGTH = 20;\n\nexport interface Options {\n prefix?: string;\n size?: number;\n}\n\nexport function uid(options: Options = {}): string {\n const { prefix, size = MAX_LENGTH } = options;\n\n if (!prefix) {\n return nanoid(size);\n }\n\n const remaining = size - prefix.length;\n\n if (remaining <= 0) {\n return prefix.slice(0, size);\n }\n\n return `${prefix}${nanoid(remaining)}`;\n}\n","export interface AckExceptionOptions extends ErrorOptions {\n errorCode: string;\n severity?: string;\n}\n\ntype AckCode = \"AE\" | \"AR\";\n\nexport abstract class AckException extends Error {\n abstract readonly code: AckCode;\n readonly errorCode: string;\n readonly severity: string | undefined;\n\n constructor(message: string, options: AckExceptionOptions) {\n super(message, { cause: options.cause });\n this.errorCode = options.errorCode;\n this.severity = options.severity;\n }\n}\n\nexport class AckError extends AckException {\n readonly code = \"AE\" as const;\n\n constructor(message: string, options: AckExceptionOptions) {\n super(message, options);\n this.name = \"AckError\";\n }\n}\n\nexport class AckReject extends AckException {\n readonly code = \"AR\" as const;\n\n constructor(message: string, options: AckExceptionOptions) {\n super(message, options);\n this.name = \"AckReject\";\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/uid.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uid.d.ts","sourceRoot":"","sources":["../src/uid.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,GAAG,CAAC,OAAO,GAAE,OAAY,GAAG,MAAM,CAcjD"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rethinkhealth/hl7v2-ack",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "HL7v2 acknowledgment message builder and typed error classes",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ack",
|
|
7
|
+
"health",
|
|
8
|
+
"healthcare",
|
|
9
|
+
"hl7",
|
|
10
|
+
"hl7v2",
|
|
11
|
+
"nodejs",
|
|
12
|
+
"typescript"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://www.rethinkhealth.io/hl7v2/docs",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "Melek Somai",
|
|
18
|
+
"email": "melek@rethinkhealth.io"
|
|
19
|
+
},
|
|
20
|
+
"repository": "rethinkhealth/hl7v2.git",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"nanoid": "5.1.6",
|
|
34
|
+
"@rethinkhealth/hl7v2-ast": "0.6.0",
|
|
35
|
+
"@rethinkhealth/hl7v2-builder": "0.6.0",
|
|
36
|
+
"@rethinkhealth/hl7v2-util-timestamp": "0.6.0",
|
|
37
|
+
"@rethinkhealth/hl7v2-util-query": "0.6.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^24.10.1",
|
|
41
|
+
"@vitest/coverage-v8": "4.0.18",
|
|
42
|
+
"tsup": "^8.5.1",
|
|
43
|
+
"typescript": "^5.9.3",
|
|
44
|
+
"vitest": "4.1.0",
|
|
45
|
+
"@rethinkhealth/hl7v2-to-hl7v2": "0.6.0",
|
|
46
|
+
"@rethinkhealth/testing": "0.0.2",
|
|
47
|
+
"@rethinkhealth/tsconfig": "0.0.1"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
52
|
+
"packageManager": "pnpm@10.14.0",
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup && tsc --emitDeclarationOnly",
|
|
55
|
+
"check-types": "tsc --noEmit",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:coverage": "vitest run --coverage",
|
|
58
|
+
"test:watch": "vitest"
|
|
59
|
+
}
|
|
60
|
+
}
|