fhir-data-utils-ts 0.6.1 → 0.6.3
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 +15 -0
- package/README.md +6 -0
- package/dist/appointment-communication-test-data.d.ts +2 -63
- package/dist/appointment-communication-test-data.js +2 -77
- package/dist/builders/immunization-reminders.d.ts +4 -0
- package/dist/builders/immunization-reminders.js +4 -0
- package/dist/builders/index.d.ts +0 -1
- package/dist/builders/index.js +0 -1
- package/dist/claims/task-claims.d.ts +1 -0
- package/dist/claims/task-claims.js +1 -0
- package/dist/data/appointment-communication.data.d.ts +63 -0
- package/dist/data/appointment-communication.data.js +77 -0
- package/dist/data/encounter-transfer.data.d.ts +41 -0
- package/dist/data/encounter-transfer.data.js +38 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.js +3 -0
- package/dist/encounter-transfer-test-data.d.ts +2 -40
- package/dist/encounter-transfer-test-data.js +2 -37
- package/dist/fhir-instant.d.ts +8 -0
- package/dist/fhir-instant.js +16 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.js +2 -3
- package/docs/ENCOUNTER_IDENTIFIERS_AND_TIME.md +19 -0
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Publish repository-safe Appointment/Communication and Encounter-transfer
|
|
6
|
+
fixtures from the explicit `fhir-data-utils-ts/data` entry point. Their
|
|
7
|
+
`*.data.ts` implementations are excluded from executable coverage while the
|
|
8
|
+
historical source modules remain compatibility reexports.
|
|
9
|
+
- Remove notification-channel policy from the general root and `builders`
|
|
10
|
+
surfaces. The historical reminder subpath and non-standard `Task.channel`
|
|
11
|
+
claim remain deprecated compatibility only while product packages migrate;
|
|
12
|
+
SOS/Vet/UHC own channel choices and dispatch behavior.
|
|
13
|
+
|
|
14
|
+
## 0.6.2 - 2026-09-30
|
|
15
|
+
|
|
16
|
+
- Add strict FHIR `instant` to Unix epoch-second derivation while preserving
|
|
17
|
+
the boundary between clinical time, `Encounter.id`, provider business
|
|
18
|
+
identifiers and human display codes.
|
|
19
|
+
|
|
5
20
|
## 0.6.1 - 2026-09-30
|
|
6
21
|
|
|
7
22
|
- Add canonical flat claims, a claims-first builder and native R5 projection
|
package/README.md
CHANGED
|
@@ -92,6 +92,12 @@ metadata and protected endpoint references only. DICOM instances and pixel
|
|
|
92
92
|
payloads remain outside this neutral package and are never embedded as flat
|
|
93
93
|
claims.
|
|
94
94
|
|
|
95
|
+
FHIR instants, resource ids and provider business identifiers remain separate.
|
|
96
|
+
Use `fhirInstantToEpochSeconds(...)` only when a downstream convention needs a
|
|
97
|
+
derived whole-second value; retain the original timezone-bearing instant and
|
|
98
|
+
an independent `Encounter.id`. See
|
|
99
|
+
[`docs/ENCOUNTER_IDENTIFIERS_AND_TIME.md`](docs/ENCOUNTER_IDENTIFIERS_AND_TIME.md).
|
|
100
|
+
|
|
95
101
|
No consumer should copy claim paths or rebuild these structures with literals.
|
|
96
102
|
Use `fhir-data-utils-ts/claims`, `/converters`, `/fhir-bundle`,
|
|
97
103
|
`/fhir-search-parameters`, `/clinical-terminology`, `/group-r5` or
|
|
@@ -1,63 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare const FHIR_APPOINTMENT_COMMUNICATION_TEST_DATA: Readonly<{
|
|
4
|
-
release: "r5";
|
|
5
|
-
start: "2026-10-05T16:00:00.000Z";
|
|
6
|
-
end: "2026-10-05T16:30:00.000Z";
|
|
7
|
-
communicationId: "communication-one";
|
|
8
|
-
subjectReference: "Patient/animal-one";
|
|
9
|
-
senderReference: "Organization/clinic-one";
|
|
10
|
-
recipientReference: "RelatedPerson/controller-one";
|
|
11
|
-
appointmentReference: "Appointment/appointment-one";
|
|
12
|
-
calendarData: string;
|
|
13
|
-
appointment: Readonly<{
|
|
14
|
-
[key: string]: unknown;
|
|
15
|
-
resourceType: string;
|
|
16
|
-
id?: string;
|
|
17
|
-
language?: string;
|
|
18
|
-
meta?: Readonly<Record<string, unknown>>;
|
|
19
|
-
contained?: readonly FhirResource[];
|
|
20
|
-
}>;
|
|
21
|
-
calendarDocument: Readonly<{
|
|
22
|
-
[key: string]: unknown;
|
|
23
|
-
resourceType: string;
|
|
24
|
-
id?: string;
|
|
25
|
-
language?: string;
|
|
26
|
-
meta?: Readonly<Record<string, unknown>>;
|
|
27
|
-
contained?: readonly FhirResource[];
|
|
28
|
-
}>;
|
|
29
|
-
communication: Readonly<{
|
|
30
|
-
[key: string]: unknown;
|
|
31
|
-
resourceType: string;
|
|
32
|
-
id?: string;
|
|
33
|
-
language?: string;
|
|
34
|
-
meta?: Readonly<Record<string, unknown>>;
|
|
35
|
-
contained?: readonly FhirResource[];
|
|
36
|
-
}>;
|
|
37
|
-
outerBatch: FhirBundle;
|
|
38
|
-
outerBatchWithInvalidDocument: FhirBundle;
|
|
39
|
-
outerBatchWithInvalidInnerBatch: FhirBundle;
|
|
40
|
-
outerBatchWithBareAppointment: Readonly<{
|
|
41
|
-
resourceType: "Bundle";
|
|
42
|
-
type: "batch";
|
|
43
|
-
entry: {
|
|
44
|
-
resource: {
|
|
45
|
-
payload: {
|
|
46
|
-
contentAttachment: {
|
|
47
|
-
contentType: string;
|
|
48
|
-
data: string;
|
|
49
|
-
};
|
|
50
|
-
}[];
|
|
51
|
-
resourceType: string;
|
|
52
|
-
id?: string;
|
|
53
|
-
language?: string;
|
|
54
|
-
meta?: Readonly<Record<string, unknown>>;
|
|
55
|
-
contained?: readonly FhirResource[];
|
|
56
|
-
};
|
|
57
|
-
request: {
|
|
58
|
-
method: string;
|
|
59
|
-
url: string;
|
|
60
|
-
};
|
|
61
|
-
}[];
|
|
62
|
-
}>;
|
|
63
|
-
}>;
|
|
1
|
+
/** @deprecated Import reusable fixtures from `fhir-data-utils-ts/data`. */
|
|
2
|
+
export * from './data/appointment-communication.data.js';
|
|
@@ -1,77 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const CALENDAR_DOCUMENT_ID = 'appointment-calendar-one';
|
|
4
|
-
const SUBJECT_REFERENCE = 'Patient/animal-one';
|
|
5
|
-
const SENDER_REFERENCE = 'Organization/clinic-one';
|
|
6
|
-
const RECIPIENT_REFERENCE = 'RelatedPerson/controller-one';
|
|
7
|
-
const APPOINTMENT_REFERENCE = `Appointment/${APPOINTMENT_ID}`;
|
|
8
|
-
const CALENDAR_DOCUMENT_REFERENCE = `DocumentReference/${CALENDAR_DOCUMENT_ID}`;
|
|
9
|
-
const APPOINTMENT_START = '2026-10-05T16:00:00.000Z';
|
|
10
|
-
const APPOINTMENT_END = '2026-10-05T16:30:00.000Z';
|
|
11
|
-
const CALENDAR_DATA = encodeBase64Utf8('BEGIN:VCALENDAR\r\nVERSION:2.0\r\nBEGIN:VEVENT\r\nDTSTART:20261005T160000Z\r\nDTEND:20261005T163000Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n');
|
|
12
|
-
const APPOINTMENT = Object.freeze({
|
|
13
|
-
resourceType: 'Appointment', id: APPOINTMENT_ID, identifier: [{ value: APPOINTMENT_ID }], status: 'booked',
|
|
14
|
-
start: APPOINTMENT_START, end: APPOINTMENT_END,
|
|
15
|
-
participant: [{ actor: { reference: SUBJECT_REFERENCE }, status: 'accepted' }, { actor: { reference: 'PractitionerRole/veterinarian-one' }, status: 'accepted' }, { actor: { reference: 'Location/consultation-one' }, status: 'accepted' }],
|
|
16
|
-
slot: [{ reference: 'Slot/consultation-one' }], supportingInformation: [{ reference: CALENDAR_DOCUMENT_REFERENCE }],
|
|
17
|
-
});
|
|
18
|
-
const CALENDAR_DOCUMENT = Object.freeze({
|
|
19
|
-
resourceType: 'DocumentReference', id: CALENDAR_DOCUMENT_ID, identifier: [{ value: CALENDAR_DOCUMENT_ID }], status: 'current',
|
|
20
|
-
subject: { reference: SUBJECT_REFERENCE }, context: { related: [{ reference: APPOINTMENT_REFERENCE }] },
|
|
21
|
-
content: [{ attachment: { contentType: 'text/calendar', data: CALENDAR_DATA, title: 'appointment.ics' } }],
|
|
22
|
-
});
|
|
23
|
-
const INNER_BUNDLE = Object.freeze({ resourceType: 'Bundle', type: 'collection', entry: [{ resource: APPOINTMENT }, { resource: CALENDAR_DOCUMENT }] });
|
|
24
|
-
const COMMUNICATION = Object.freeze({
|
|
25
|
-
resourceType: 'Communication', id: COMMUNICATION_ID, identifier: [{ value: COMMUNICATION_ID }], status: 'completed',
|
|
26
|
-
subject: { reference: SUBJECT_REFERENCE }, recipient: [{ reference: RECIPIENT_REFERENCE }], sender: { reference: SENDER_REFERENCE },
|
|
27
|
-
payload: [{ contentAttachment: { contentType: 'application/fhir+json', data: encodeBase64Utf8(JSON.stringify(INNER_BUNDLE)), title: 'appointment-bundle.json' } }],
|
|
28
|
-
});
|
|
29
|
-
function communicationWithInnerBundle(innerBundle) {
|
|
30
|
-
return Object.freeze({
|
|
31
|
-
...COMMUNICATION,
|
|
32
|
-
payload: [{ contentAttachment: { contentType: 'application/fhir+json', data: encodeBase64Utf8(JSON.stringify(innerBundle)) } }],
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
function outerBatchWithCommunication(communication) {
|
|
36
|
-
return Object.freeze({
|
|
37
|
-
resourceType: 'Bundle',
|
|
38
|
-
type: 'batch',
|
|
39
|
-
entry: [{ resource: communication, request: { method: 'POST', url: 'Communication' } }],
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
/** Repository-safe reusable evidence for appointment Bundle and calendar tests. */
|
|
43
|
-
export const FHIR_APPOINTMENT_COMMUNICATION_TEST_DATA = Object.freeze({
|
|
44
|
-
release: 'r5',
|
|
45
|
-
start: APPOINTMENT_START,
|
|
46
|
-
end: APPOINTMENT_END,
|
|
47
|
-
communicationId: COMMUNICATION_ID,
|
|
48
|
-
subjectReference: SUBJECT_REFERENCE,
|
|
49
|
-
senderReference: SENDER_REFERENCE,
|
|
50
|
-
recipientReference: RECIPIENT_REFERENCE,
|
|
51
|
-
appointmentReference: APPOINTMENT_REFERENCE,
|
|
52
|
-
calendarData: CALENDAR_DATA,
|
|
53
|
-
appointment: APPOINTMENT,
|
|
54
|
-
calendarDocument: CALENDAR_DOCUMENT,
|
|
55
|
-
communication: COMMUNICATION,
|
|
56
|
-
outerBatch: outerBatchWithCommunication(COMMUNICATION),
|
|
57
|
-
outerBatchWithInvalidDocument: outerBatchWithCommunication(communicationWithInnerBundle(Object.freeze({
|
|
58
|
-
resourceType: 'Bundle',
|
|
59
|
-
type: 'document',
|
|
60
|
-
entry: [{ resource: APPOINTMENT }],
|
|
61
|
-
}))),
|
|
62
|
-
outerBatchWithInvalidInnerBatch: outerBatchWithCommunication(communicationWithInnerBundle(Object.freeze({
|
|
63
|
-
resourceType: 'Bundle',
|
|
64
|
-
type: 'batch',
|
|
65
|
-
entry: [{ resource: APPOINTMENT }],
|
|
66
|
-
}))),
|
|
67
|
-
outerBatchWithBareAppointment: Object.freeze({
|
|
68
|
-
resourceType: 'Bundle', type: 'batch', entry: [{ resource: { ...COMMUNICATION, payload: [{ contentAttachment: { contentType: 'application/fhir+json', data: encodeBase64Utf8(JSON.stringify(APPOINTMENT)) } }] }, request: { method: 'POST', url: 'Communication' } }],
|
|
69
|
-
}),
|
|
70
|
-
});
|
|
71
|
-
function encodeBase64Utf8(value) {
|
|
72
|
-
const bytes = new TextEncoder().encode(value);
|
|
73
|
-
let binary = '';
|
|
74
|
-
for (const byte of bytes)
|
|
75
|
-
binary += String.fromCharCode(byte);
|
|
76
|
-
return globalThis.btoa(binary);
|
|
77
|
-
}
|
|
1
|
+
/** @deprecated Import reusable fixtures from `fhir-data-utils-ts/data`. */
|
|
2
|
+
export * from './data/appointment-communication.data.js';
|
|
@@ -5,6 +5,10 @@ export declare const ImmunizationReminderTaskValues: Readonly<{
|
|
|
5
5
|
readonly Code: "immunization-reminder";
|
|
6
6
|
readonly TriggerType: "scheduled";
|
|
7
7
|
}>;
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Compatibility only. Notification-channel policy belongs to the
|
|
10
|
+
* consuming SOS/Vet/UHC data package, not to the general FHIR data package.
|
|
11
|
+
*/
|
|
8
12
|
export declare const ImmunizationReminderChannels: readonly ["push", "email", "sms", "whatsapp", "telephone"];
|
|
9
13
|
export type ImmunizationReminderChannel = (typeof ImmunizationReminderChannels)[number];
|
|
10
14
|
export type ImmunizationReminderPlanInput = Readonly<{
|
|
@@ -8,6 +8,10 @@ export const ImmunizationReminderTaskValues = Object.freeze({
|
|
|
8
8
|
Code: 'immunization-reminder',
|
|
9
9
|
TriggerType: 'scheduled',
|
|
10
10
|
});
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated Compatibility only. Notification-channel policy belongs to the
|
|
13
|
+
* consuming SOS/Vet/UHC data package, not to the general FHIR data package.
|
|
14
|
+
*/
|
|
11
15
|
export const ImmunizationReminderChannels = Object.freeze([
|
|
12
16
|
'push',
|
|
13
17
|
'email',
|
package/dist/builders/index.d.ts
CHANGED
package/dist/builders/index.js
CHANGED
|
@@ -21,6 +21,7 @@ export declare enum TaskClaimsFhirApi {
|
|
|
21
21
|
BasedOn = "Task.based-on",
|
|
22
22
|
BasedOnDisplay = "Task.based-on-display",
|
|
23
23
|
Language = "Task.language",
|
|
24
|
+
/** @deprecated Non-standard compatibility claim. Model delivery policy outside FHIR. */
|
|
24
25
|
Channel = "Task.channel",
|
|
25
26
|
AuthoredOn = "Task.authored-on",
|
|
26
27
|
LastModified = "Task.last-modified",
|
|
@@ -24,6 +24,7 @@ export var TaskClaimsFhirApi;
|
|
|
24
24
|
TaskClaimsFhirApi["BasedOn"] = "Task.based-on";
|
|
25
25
|
TaskClaimsFhirApi["BasedOnDisplay"] = "Task.based-on-display";
|
|
26
26
|
TaskClaimsFhirApi["Language"] = "Task.language";
|
|
27
|
+
/** @deprecated Non-standard compatibility claim. Model delivery policy outside FHIR. */
|
|
27
28
|
TaskClaimsFhirApi["Channel"] = "Task.channel";
|
|
28
29
|
TaskClaimsFhirApi["AuthoredOn"] = "Task.authored-on";
|
|
29
30
|
TaskClaimsFhirApi["LastModified"] = "Task.last-modified";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { FhirBundle, FhirResource } from '../fhir-types.js';
|
|
2
|
+
/** Repository-safe reusable evidence for appointment Bundle and calendar tests. */
|
|
3
|
+
export declare const FHIR_APPOINTMENT_COMMUNICATION_TEST_DATA: Readonly<{
|
|
4
|
+
release: "r5";
|
|
5
|
+
start: "2026-10-05T16:00:00.000Z";
|
|
6
|
+
end: "2026-10-05T16:30:00.000Z";
|
|
7
|
+
communicationId: "communication-one";
|
|
8
|
+
subjectReference: "Patient/animal-one";
|
|
9
|
+
senderReference: "Organization/clinic-one";
|
|
10
|
+
recipientReference: "RelatedPerson/controller-one";
|
|
11
|
+
appointmentReference: "Appointment/appointment-one";
|
|
12
|
+
calendarData: string;
|
|
13
|
+
appointment: Readonly<{
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
resourceType: string;
|
|
16
|
+
id?: string;
|
|
17
|
+
language?: string;
|
|
18
|
+
meta?: Readonly<Record<string, unknown>>;
|
|
19
|
+
contained?: readonly FhirResource[];
|
|
20
|
+
}>;
|
|
21
|
+
calendarDocument: Readonly<{
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
resourceType: string;
|
|
24
|
+
id?: string;
|
|
25
|
+
language?: string;
|
|
26
|
+
meta?: Readonly<Record<string, unknown>>;
|
|
27
|
+
contained?: readonly FhirResource[];
|
|
28
|
+
}>;
|
|
29
|
+
communication: Readonly<{
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
resourceType: string;
|
|
32
|
+
id?: string;
|
|
33
|
+
language?: string;
|
|
34
|
+
meta?: Readonly<Record<string, unknown>>;
|
|
35
|
+
contained?: readonly FhirResource[];
|
|
36
|
+
}>;
|
|
37
|
+
outerBatch: FhirBundle;
|
|
38
|
+
outerBatchWithInvalidDocument: FhirBundle;
|
|
39
|
+
outerBatchWithInvalidInnerBatch: FhirBundle;
|
|
40
|
+
outerBatchWithBareAppointment: Readonly<{
|
|
41
|
+
resourceType: "Bundle";
|
|
42
|
+
type: "batch";
|
|
43
|
+
entry: {
|
|
44
|
+
resource: {
|
|
45
|
+
payload: {
|
|
46
|
+
contentAttachment: {
|
|
47
|
+
contentType: string;
|
|
48
|
+
data: string;
|
|
49
|
+
};
|
|
50
|
+
}[];
|
|
51
|
+
resourceType: string;
|
|
52
|
+
id?: string;
|
|
53
|
+
language?: string;
|
|
54
|
+
meta?: Readonly<Record<string, unknown>>;
|
|
55
|
+
contained?: readonly FhirResource[];
|
|
56
|
+
};
|
|
57
|
+
request: {
|
|
58
|
+
method: string;
|
|
59
|
+
url: string;
|
|
60
|
+
};
|
|
61
|
+
}[];
|
|
62
|
+
}>;
|
|
63
|
+
}>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const APPOINTMENT_ID = 'appointment-one';
|
|
2
|
+
const COMMUNICATION_ID = 'communication-one';
|
|
3
|
+
const CALENDAR_DOCUMENT_ID = 'appointment-calendar-one';
|
|
4
|
+
const SUBJECT_REFERENCE = 'Patient/animal-one';
|
|
5
|
+
const SENDER_REFERENCE = 'Organization/clinic-one';
|
|
6
|
+
const RECIPIENT_REFERENCE = 'RelatedPerson/controller-one';
|
|
7
|
+
const APPOINTMENT_REFERENCE = `Appointment/${APPOINTMENT_ID}`;
|
|
8
|
+
const CALENDAR_DOCUMENT_REFERENCE = `DocumentReference/${CALENDAR_DOCUMENT_ID}`;
|
|
9
|
+
const APPOINTMENT_START = '2026-10-05T16:00:00.000Z';
|
|
10
|
+
const APPOINTMENT_END = '2026-10-05T16:30:00.000Z';
|
|
11
|
+
const CALENDAR_DATA = encodeBase64Utf8('BEGIN:VCALENDAR\r\nVERSION:2.0\r\nBEGIN:VEVENT\r\nDTSTART:20261005T160000Z\r\nDTEND:20261005T163000Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n');
|
|
12
|
+
const APPOINTMENT = Object.freeze({
|
|
13
|
+
resourceType: 'Appointment', id: APPOINTMENT_ID, identifier: [{ value: APPOINTMENT_ID }], status: 'booked',
|
|
14
|
+
start: APPOINTMENT_START, end: APPOINTMENT_END,
|
|
15
|
+
participant: [{ actor: { reference: SUBJECT_REFERENCE }, status: 'accepted' }, { actor: { reference: 'PractitionerRole/veterinarian-one' }, status: 'accepted' }, { actor: { reference: 'Location/consultation-one' }, status: 'accepted' }],
|
|
16
|
+
slot: [{ reference: 'Slot/consultation-one' }], supportingInformation: [{ reference: CALENDAR_DOCUMENT_REFERENCE }],
|
|
17
|
+
});
|
|
18
|
+
const CALENDAR_DOCUMENT = Object.freeze({
|
|
19
|
+
resourceType: 'DocumentReference', id: CALENDAR_DOCUMENT_ID, identifier: [{ value: CALENDAR_DOCUMENT_ID }], status: 'current',
|
|
20
|
+
subject: { reference: SUBJECT_REFERENCE }, context: { related: [{ reference: APPOINTMENT_REFERENCE }] },
|
|
21
|
+
content: [{ attachment: { contentType: 'text/calendar', data: CALENDAR_DATA, title: 'appointment.ics' } }],
|
|
22
|
+
});
|
|
23
|
+
const INNER_BUNDLE = Object.freeze({ resourceType: 'Bundle', type: 'collection', entry: [{ resource: APPOINTMENT }, { resource: CALENDAR_DOCUMENT }] });
|
|
24
|
+
const COMMUNICATION = Object.freeze({
|
|
25
|
+
resourceType: 'Communication', id: COMMUNICATION_ID, identifier: [{ value: COMMUNICATION_ID }], status: 'completed',
|
|
26
|
+
subject: { reference: SUBJECT_REFERENCE }, recipient: [{ reference: RECIPIENT_REFERENCE }], sender: { reference: SENDER_REFERENCE },
|
|
27
|
+
payload: [{ contentAttachment: { contentType: 'application/fhir+json', data: encodeBase64Utf8(JSON.stringify(INNER_BUNDLE)), title: 'appointment-bundle.json' } }],
|
|
28
|
+
});
|
|
29
|
+
function communicationWithInnerBundle(innerBundle) {
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
...COMMUNICATION,
|
|
32
|
+
payload: [{ contentAttachment: { contentType: 'application/fhir+json', data: encodeBase64Utf8(JSON.stringify(innerBundle)) } }],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function outerBatchWithCommunication(communication) {
|
|
36
|
+
return Object.freeze({
|
|
37
|
+
resourceType: 'Bundle',
|
|
38
|
+
type: 'batch',
|
|
39
|
+
entry: [{ resource: communication, request: { method: 'POST', url: 'Communication' } }],
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Repository-safe reusable evidence for appointment Bundle and calendar tests. */
|
|
43
|
+
export const FHIR_APPOINTMENT_COMMUNICATION_TEST_DATA = Object.freeze({
|
|
44
|
+
release: 'r5',
|
|
45
|
+
start: APPOINTMENT_START,
|
|
46
|
+
end: APPOINTMENT_END,
|
|
47
|
+
communicationId: COMMUNICATION_ID,
|
|
48
|
+
subjectReference: SUBJECT_REFERENCE,
|
|
49
|
+
senderReference: SENDER_REFERENCE,
|
|
50
|
+
recipientReference: RECIPIENT_REFERENCE,
|
|
51
|
+
appointmentReference: APPOINTMENT_REFERENCE,
|
|
52
|
+
calendarData: CALENDAR_DATA,
|
|
53
|
+
appointment: APPOINTMENT,
|
|
54
|
+
calendarDocument: CALENDAR_DOCUMENT,
|
|
55
|
+
communication: COMMUNICATION,
|
|
56
|
+
outerBatch: outerBatchWithCommunication(COMMUNICATION),
|
|
57
|
+
outerBatchWithInvalidDocument: outerBatchWithCommunication(communicationWithInnerBundle(Object.freeze({
|
|
58
|
+
resourceType: 'Bundle',
|
|
59
|
+
type: 'document',
|
|
60
|
+
entry: [{ resource: APPOINTMENT }],
|
|
61
|
+
}))),
|
|
62
|
+
outerBatchWithInvalidInnerBatch: outerBatchWithCommunication(communicationWithInnerBundle(Object.freeze({
|
|
63
|
+
resourceType: 'Bundle',
|
|
64
|
+
type: 'batch',
|
|
65
|
+
entry: [{ resource: APPOINTMENT }],
|
|
66
|
+
}))),
|
|
67
|
+
outerBatchWithBareAppointment: Object.freeze({
|
|
68
|
+
resourceType: 'Bundle', type: 'batch', entry: [{ resource: { ...COMMUNICATION, payload: [{ contentAttachment: { contentType: 'application/fhir+json', data: encodeBase64Utf8(JSON.stringify(APPOINTMENT)) } }] }, request: { method: 'POST', url: 'Communication' } }],
|
|
69
|
+
}),
|
|
70
|
+
});
|
|
71
|
+
function encodeBase64Utf8(value) {
|
|
72
|
+
const bytes = new TextEncoder().encode(value);
|
|
73
|
+
let binary = '';
|
|
74
|
+
for (const byte of bytes)
|
|
75
|
+
binary += String.fromCharCode(byte);
|
|
76
|
+
return globalThis.btoa(binary);
|
|
77
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { EncounterStatusR5 } from '../claims/encounter-claims.js';
|
|
2
|
+
/** Product-neutral identifiers and references for cross-package Encounter transfer contract tests. */
|
|
3
|
+
export declare const FHIR_ENCOUNTER_TRANSFER_TEST_DATA: Readonly<{
|
|
4
|
+
communicationIdentifier: "urn:uuid:00000000-0000-4000-8000-000000000401";
|
|
5
|
+
subjectReference: "Patient/00000000-0000-4000-8000-000000000402";
|
|
6
|
+
senderReference: "Organization/00000000-0000-4000-8000-000000000403";
|
|
7
|
+
indexRecipientReference: "Organization/00000000-0000-4000-8000-000000000404";
|
|
8
|
+
occurredAt: "2026-09-30T10:10:00Z";
|
|
9
|
+
invalidOccurredAtWithoutTimezone: "2026-09-30T10:10:00";
|
|
10
|
+
class: Readonly<{
|
|
11
|
+
system: "http://terminology.hl7.org/CodeSystem/v3-ActCode";
|
|
12
|
+
code: "EMER";
|
|
13
|
+
}>;
|
|
14
|
+
source: Readonly<{
|
|
15
|
+
id: "00000000-0000-4000-8000-000000000405";
|
|
16
|
+
reference: "Encounter/00000000-0000-4000-8000-000000000405";
|
|
17
|
+
identifier: "urn:uuid:00000000-0000-4000-8000-000000000405";
|
|
18
|
+
serviceReference: "HealthcareService/00000000-0000-4000-8000-000000000406";
|
|
19
|
+
locationReference: "Location/00000000-0000-4000-8000-000000000407";
|
|
20
|
+
statusWhenReturning: EncounterStatusR5.OnHold;
|
|
21
|
+
statusWhenNotReturning: EncounterStatusR5.Completed;
|
|
22
|
+
}>;
|
|
23
|
+
destination: Readonly<{
|
|
24
|
+
id: "00000000-0000-4000-8000-000000000408";
|
|
25
|
+
reference: "Encounter/00000000-0000-4000-8000-000000000408";
|
|
26
|
+
identifier: "urn:uuid:00000000-0000-4000-8000-000000000408";
|
|
27
|
+
serviceReference: "HealthcareService/00000000-0000-4000-8000-000000000409";
|
|
28
|
+
locationReference: "Location/00000000-0000-4000-8000-000000000410";
|
|
29
|
+
statusOnArrival: EncounterStatusR5.InProgress;
|
|
30
|
+
}>;
|
|
31
|
+
sourceHistory: Readonly<{
|
|
32
|
+
id: "00000000-0000-4000-8000-000000000411";
|
|
33
|
+
identifier: "urn:uuid:00000000-0000-4000-8000-000000000411";
|
|
34
|
+
encounterReference: "Encounter/00000000-0000-4000-8000-000000000405";
|
|
35
|
+
}>;
|
|
36
|
+
destinationHistory: Readonly<{
|
|
37
|
+
id: "00000000-0000-4000-8000-000000000412";
|
|
38
|
+
identifier: "urn:uuid:00000000-0000-4000-8000-000000000412";
|
|
39
|
+
encounterReference: "Encounter/00000000-0000-4000-8000-000000000408";
|
|
40
|
+
}>;
|
|
41
|
+
}>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { EncounterStatusR5 } from '../claims/encounter-claims.js';
|
|
2
|
+
/** Product-neutral identifiers and references for cross-package Encounter transfer contract tests. */
|
|
3
|
+
export const FHIR_ENCOUNTER_TRANSFER_TEST_DATA = Object.freeze({
|
|
4
|
+
communicationIdentifier: 'urn:uuid:00000000-0000-4000-8000-000000000401',
|
|
5
|
+
subjectReference: 'Patient/00000000-0000-4000-8000-000000000402',
|
|
6
|
+
senderReference: 'Organization/00000000-0000-4000-8000-000000000403',
|
|
7
|
+
indexRecipientReference: 'Organization/00000000-0000-4000-8000-000000000404',
|
|
8
|
+
occurredAt: '2026-09-30T10:10:00Z',
|
|
9
|
+
invalidOccurredAtWithoutTimezone: '2026-09-30T10:10:00',
|
|
10
|
+
class: Object.freeze({ system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'EMER' }),
|
|
11
|
+
source: Object.freeze({
|
|
12
|
+
id: '00000000-0000-4000-8000-000000000405',
|
|
13
|
+
reference: 'Encounter/00000000-0000-4000-8000-000000000405',
|
|
14
|
+
identifier: 'urn:uuid:00000000-0000-4000-8000-000000000405',
|
|
15
|
+
serviceReference: 'HealthcareService/00000000-0000-4000-8000-000000000406',
|
|
16
|
+
locationReference: 'Location/00000000-0000-4000-8000-000000000407',
|
|
17
|
+
statusWhenReturning: EncounterStatusR5.OnHold,
|
|
18
|
+
statusWhenNotReturning: EncounterStatusR5.Completed,
|
|
19
|
+
}),
|
|
20
|
+
destination: Object.freeze({
|
|
21
|
+
id: '00000000-0000-4000-8000-000000000408',
|
|
22
|
+
reference: 'Encounter/00000000-0000-4000-8000-000000000408',
|
|
23
|
+
identifier: 'urn:uuid:00000000-0000-4000-8000-000000000408',
|
|
24
|
+
serviceReference: 'HealthcareService/00000000-0000-4000-8000-000000000409',
|
|
25
|
+
locationReference: 'Location/00000000-0000-4000-8000-000000000410',
|
|
26
|
+
statusOnArrival: EncounterStatusR5.InProgress,
|
|
27
|
+
}),
|
|
28
|
+
sourceHistory: Object.freeze({
|
|
29
|
+
id: '00000000-0000-4000-8000-000000000411',
|
|
30
|
+
identifier: 'urn:uuid:00000000-0000-4000-8000-000000000411',
|
|
31
|
+
encounterReference: 'Encounter/00000000-0000-4000-8000-000000000405',
|
|
32
|
+
}),
|
|
33
|
+
destinationHistory: Object.freeze({
|
|
34
|
+
id: '00000000-0000-4000-8000-000000000412',
|
|
35
|
+
identifier: 'urn:uuid:00000000-0000-4000-8000-000000000412',
|
|
36
|
+
encounterReference: 'Encounter/00000000-0000-4000-8000-000000000408',
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
@@ -1,40 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare const FHIR_ENCOUNTER_TRANSFER_TEST_DATA: Readonly<{
|
|
4
|
-
communicationIdentifier: "urn:uuid:00000000-0000-4000-8000-000000000401";
|
|
5
|
-
subjectReference: "Patient/00000000-0000-4000-8000-000000000402";
|
|
6
|
-
senderReference: "Organization/00000000-0000-4000-8000-000000000403";
|
|
7
|
-
indexRecipientReference: "Organization/00000000-0000-4000-8000-000000000404";
|
|
8
|
-
occurredAt: "2026-09-30T10:10:00Z";
|
|
9
|
-
class: Readonly<{
|
|
10
|
-
system: "http://terminology.hl7.org/CodeSystem/v3-ActCode";
|
|
11
|
-
code: "EMER";
|
|
12
|
-
}>;
|
|
13
|
-
source: Readonly<{
|
|
14
|
-
id: "00000000-0000-4000-8000-000000000405";
|
|
15
|
-
reference: "Encounter/00000000-0000-4000-8000-000000000405";
|
|
16
|
-
identifier: "urn:uuid:00000000-0000-4000-8000-000000000405";
|
|
17
|
-
serviceReference: "HealthcareService/00000000-0000-4000-8000-000000000406";
|
|
18
|
-
locationReference: "Location/00000000-0000-4000-8000-000000000407";
|
|
19
|
-
statusWhenReturning: EncounterStatusR5.OnHold;
|
|
20
|
-
statusWhenNotReturning: EncounterStatusR5.Completed;
|
|
21
|
-
}>;
|
|
22
|
-
destination: Readonly<{
|
|
23
|
-
id: "00000000-0000-4000-8000-000000000408";
|
|
24
|
-
reference: "Encounter/00000000-0000-4000-8000-000000000408";
|
|
25
|
-
identifier: "urn:uuid:00000000-0000-4000-8000-000000000408";
|
|
26
|
-
serviceReference: "HealthcareService/00000000-0000-4000-8000-000000000409";
|
|
27
|
-
locationReference: "Location/00000000-0000-4000-8000-000000000410";
|
|
28
|
-
statusOnArrival: EncounterStatusR5.InProgress;
|
|
29
|
-
}>;
|
|
30
|
-
sourceHistory: Readonly<{
|
|
31
|
-
id: "00000000-0000-4000-8000-000000000411";
|
|
32
|
-
identifier: "urn:uuid:00000000-0000-4000-8000-000000000411";
|
|
33
|
-
encounterReference: "Encounter/00000000-0000-4000-8000-000000000405";
|
|
34
|
-
}>;
|
|
35
|
-
destinationHistory: Readonly<{
|
|
36
|
-
id: "00000000-0000-4000-8000-000000000412";
|
|
37
|
-
identifier: "urn:uuid:00000000-0000-4000-8000-000000000412";
|
|
38
|
-
encounterReference: "Encounter/00000000-0000-4000-8000-000000000408";
|
|
39
|
-
}>;
|
|
40
|
-
}>;
|
|
1
|
+
/** @deprecated Import reusable fixtures from `fhir-data-utils-ts/data`. */
|
|
2
|
+
export * from './data/encounter-transfer.data.js';
|
|
@@ -1,37 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export const FHIR_ENCOUNTER_TRANSFER_TEST_DATA = Object.freeze({
|
|
4
|
-
communicationIdentifier: 'urn:uuid:00000000-0000-4000-8000-000000000401',
|
|
5
|
-
subjectReference: 'Patient/00000000-0000-4000-8000-000000000402',
|
|
6
|
-
senderReference: 'Organization/00000000-0000-4000-8000-000000000403',
|
|
7
|
-
indexRecipientReference: 'Organization/00000000-0000-4000-8000-000000000404',
|
|
8
|
-
occurredAt: '2026-09-30T10:10:00Z',
|
|
9
|
-
class: Object.freeze({ system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', code: 'EMER' }),
|
|
10
|
-
source: Object.freeze({
|
|
11
|
-
id: '00000000-0000-4000-8000-000000000405',
|
|
12
|
-
reference: 'Encounter/00000000-0000-4000-8000-000000000405',
|
|
13
|
-
identifier: 'urn:uuid:00000000-0000-4000-8000-000000000405',
|
|
14
|
-
serviceReference: 'HealthcareService/00000000-0000-4000-8000-000000000406',
|
|
15
|
-
locationReference: 'Location/00000000-0000-4000-8000-000000000407',
|
|
16
|
-
statusWhenReturning: EncounterStatusR5.OnHold,
|
|
17
|
-
statusWhenNotReturning: EncounterStatusR5.Completed,
|
|
18
|
-
}),
|
|
19
|
-
destination: Object.freeze({
|
|
20
|
-
id: '00000000-0000-4000-8000-000000000408',
|
|
21
|
-
reference: 'Encounter/00000000-0000-4000-8000-000000000408',
|
|
22
|
-
identifier: 'urn:uuid:00000000-0000-4000-8000-000000000408',
|
|
23
|
-
serviceReference: 'HealthcareService/00000000-0000-4000-8000-000000000409',
|
|
24
|
-
locationReference: 'Location/00000000-0000-4000-8000-000000000410',
|
|
25
|
-
statusOnArrival: EncounterStatusR5.InProgress,
|
|
26
|
-
}),
|
|
27
|
-
sourceHistory: Object.freeze({
|
|
28
|
-
id: '00000000-0000-4000-8000-000000000411',
|
|
29
|
-
identifier: 'urn:uuid:00000000-0000-4000-8000-000000000411',
|
|
30
|
-
encounterReference: 'Encounter/00000000-0000-4000-8000-000000000405',
|
|
31
|
-
}),
|
|
32
|
-
destinationHistory: Object.freeze({
|
|
33
|
-
id: '00000000-0000-4000-8000-000000000412',
|
|
34
|
-
identifier: 'urn:uuid:00000000-0000-4000-8000-000000000412',
|
|
35
|
-
encounterReference: 'Encounter/00000000-0000-4000-8000-000000000408',
|
|
36
|
-
}),
|
|
37
|
-
});
|
|
1
|
+
/** @deprecated Import reusable fixtures from `fhir-data-utils-ts/data`. */
|
|
2
|
+
export * from './data/encounter-transfer.data.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derives whole Unix epoch seconds from a valid FHIR `instant`.
|
|
3
|
+
*
|
|
4
|
+
* A FHIR instant remains the authoritative ISO 8601 value with a timezone.
|
|
5
|
+
* Epoch seconds are a derived numeric representation and must never replace
|
|
6
|
+
* `Encounter.id`, `Encounter.identifier`, or the original clinical time.
|
|
7
|
+
*/
|
|
8
|
+
export declare function fhirInstantToEpochSeconds(instant: string): number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const FHIR_INSTANT_PATTERN = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/;
|
|
2
|
+
/**
|
|
3
|
+
* Derives whole Unix epoch seconds from a valid FHIR `instant`.
|
|
4
|
+
*
|
|
5
|
+
* A FHIR instant remains the authoritative ISO 8601 value with a timezone.
|
|
6
|
+
* Epoch seconds are a derived numeric representation and must never replace
|
|
7
|
+
* `Encounter.id`, `Encounter.identifier`, or the original clinical time.
|
|
8
|
+
*/
|
|
9
|
+
export function fhirInstantToEpochSeconds(instant) {
|
|
10
|
+
if (!FHIR_INSTANT_PATTERN.test(instant))
|
|
11
|
+
throw new TypeError('fhir_instant_invalid');
|
|
12
|
+
const epochMilliseconds = Date.parse(instant);
|
|
13
|
+
if (!Number.isFinite(epochMilliseconds))
|
|
14
|
+
throw new TypeError('fhir_instant_invalid');
|
|
15
|
+
return Math.floor(epochMilliseconds / 1000);
|
|
16
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,6 @@ export * from './constants/index.js';
|
|
|
16
16
|
export * from './clinical-terminology.js';
|
|
17
17
|
export * from './care-emergency-alert.js';
|
|
18
18
|
export * from './immunization-flat-claims.js';
|
|
19
|
-
export * from './immunization-reminder-plan.js';
|
|
20
19
|
export * from './builders/index.js';
|
|
21
20
|
export * from './group-r5.js';
|
|
22
21
|
export * from './research-study-r5.js';
|
|
@@ -25,5 +24,5 @@ export * from './related-person.js';
|
|
|
25
24
|
export * from './imaging-types.js';
|
|
26
25
|
export * from './subscription-r5.js';
|
|
27
26
|
export * from './healthcare-service-specialty-map.js';
|
|
28
|
-
export * from './
|
|
29
|
-
export * from './
|
|
27
|
+
export * from './data/index.js';
|
|
28
|
+
export * from './fhir-instant.js';
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,6 @@ export * from './constants/index.js';
|
|
|
16
16
|
export * from './clinical-terminology.js';
|
|
17
17
|
export * from './care-emergency-alert.js';
|
|
18
18
|
export * from './immunization-flat-claims.js';
|
|
19
|
-
export * from './immunization-reminder-plan.js';
|
|
20
19
|
export * from './builders/index.js';
|
|
21
20
|
export * from './group-r5.js';
|
|
22
21
|
export * from './research-study-r5.js';
|
|
@@ -25,5 +24,5 @@ export * from './related-person.js';
|
|
|
25
24
|
export * from './imaging-types.js';
|
|
26
25
|
export * from './subscription-r5.js';
|
|
27
26
|
export * from './healthcare-service-specialty-map.js';
|
|
28
|
-
export * from './
|
|
29
|
-
export * from './
|
|
27
|
+
export * from './data/index.js';
|
|
28
|
+
export * from './fhir-instant.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Encounter identifiers and time
|
|
2
|
+
|
|
3
|
+
FHIR time and encounter identity are separate concepts.
|
|
4
|
+
|
|
5
|
+
- A FHIR `instant` is an ISO 8601 date and time with at least second precision
|
|
6
|
+
and an explicit timezone. It is not a Unix timestamp.
|
|
7
|
+
- Unix epoch seconds may be derived for sorting, interoperability or a
|
|
8
|
+
provider-owned business identifier, but they never replace the original
|
|
9
|
+
instant.
|
|
10
|
+
- `Encounter.id` is the FHIR resource identifier.
|
|
11
|
+
- `Encounter.identifier` contains the business identifiers assigned by the
|
|
12
|
+
care provider. Their namespace and uniqueness belong to the assigning
|
|
13
|
+
organization.
|
|
14
|
+
- A display code is not sufficient authorization or subject resolution. An
|
|
15
|
+
authoritative lookup must also verify the assigner and exact subject.
|
|
16
|
+
|
|
17
|
+
Reusable test data lives in `FHIR_ENCOUNTER_TRANSFER_TEST_DATA`. Tests and
|
|
18
|
+
downstream documentation import its named fields instead of duplicating raw
|
|
19
|
+
identifiers, timestamps or references.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fhir-data-utils-ts",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "FHIR-version-neutral flat-claim and SearchParameter data utilities",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -122,6 +122,10 @@
|
|
|
122
122
|
"./constants": {
|
|
123
123
|
"types": "./dist/constants/index.d.ts",
|
|
124
124
|
"default": "./dist/constants/index.js"
|
|
125
|
+
},
|
|
126
|
+
"./data": {
|
|
127
|
+
"types": "./dist/data/index.d.ts",
|
|
128
|
+
"default": "./dist/data/index.js"
|
|
125
129
|
}
|
|
126
130
|
},
|
|
127
131
|
"files": [
|
|
@@ -136,7 +140,7 @@
|
|
|
136
140
|
"build": "npm run clean && tsc -p tsconfig.json",
|
|
137
141
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
138
142
|
"test": "npm run build && node --test tests/*.test.mjs",
|
|
139
|
-
"test:coverage": "node --test --experimental-test-coverage --test-coverage-include='dist/converters/*.js' --test-coverage-include='dist/builders/*.js' --test-coverage-include='dist/fhir-*.js' --test-coverage-lines=95 --test-coverage-functions=80 --test-coverage-branches=40 tests/*.test.mjs",
|
|
143
|
+
"test:coverage": "node --test --experimental-test-coverage --test-coverage-include='dist/converters/*.js' --test-coverage-include='dist/builders/*.js' --test-coverage-include='dist/fhir-*.js' --test-coverage-exclude='dist/**/*.data.js' --test-coverage-lines=95 --test-coverage-functions=80 --test-coverage-branches=40 tests/*.test.mjs",
|
|
140
144
|
"check": "npm run typecheck && npm test && npm run test:coverage",
|
|
141
145
|
"prepack": "npm run check"
|
|
142
146
|
},
|