fhir-data-utils-ts 0.3.7 → 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/.codex/skills/govern-fhir-data-utilities/SKILL.md +35 -0
- package/.codex/skills/govern-fhir-observation-components/SKILL.md +11 -0
- package/CHANGELOG.md +63 -0
- package/README.md +44 -0
- package/dist/appointment-communication-test-data.d.ts +63 -0
- package/dist/appointment-communication-test-data.js +77 -0
- package/dist/builders/department-service-graph.d.ts +32 -0
- package/dist/builders/department-service-graph.js +102 -0
- package/dist/builders/device-compliance-graph.d.ts +51 -0
- package/dist/builders/device-compliance-graph.js +115 -0
- package/dist/builders/index.d.ts +3 -0
- package/dist/builders/index.js +3 -0
- package/dist/builders/service-schedule-graph.d.ts +58 -0
- package/dist/builders/service-schedule-graph.js +149 -0
- package/dist/claims/appointment-claims.d.ts +6 -0
- package/dist/claims/appointment-claims.js +6 -0
- package/dist/claims/composition-claims.d.ts +1 -0
- package/dist/claims/composition-claims.js +1 -0
- package/dist/claims/device-metric-claims.d.ts +16 -0
- package/dist/claims/device-metric-claims.js +25 -0
- package/dist/claims/healthcare-service-claims.d.ts +32 -0
- package/dist/claims/healthcare-service-claims.js +54 -0
- package/dist/claims/index.d.ts +4 -0
- package/dist/claims/index.js +4 -0
- package/dist/claims/location-claims.d.ts +3 -0
- package/dist/claims/location-claims.js +6 -0
- package/dist/claims/organization-claims.d.ts +3 -1
- package/dist/claims/organization-claims.js +4 -1
- package/dist/claims/procedure-claims.d.ts +2 -0
- package/dist/claims/procedure-claims.js +4 -0
- package/dist/claims/regulated-authorization-claims.d.ts +17 -0
- package/dist/claims/regulated-authorization-claims.js +27 -0
- package/dist/claims/schedule-slot-claims.d.ts +45 -0
- package/dist/claims/schedule-slot-claims.js +70 -0
- package/dist/constants/fhir-resource-types.d.ts +9 -0
- package/dist/constants/fhir-resource-types.js +3 -0
- package/dist/converters/convert-appointment-communication.d.ts +14 -0
- package/dist/converters/convert-appointment-communication.js +97 -0
- package/dist/converters/convert-appointment.js +16 -7
- package/dist/converters/convert-composition.js +12 -2
- package/dist/converters/convert-device-metric.d.ts +3 -0
- package/dist/converters/convert-device-metric.js +37 -0
- package/dist/converters/convert-document-reference.js +5 -0
- package/dist/converters/convert-healthcare-service.d.ts +7 -0
- package/dist/converters/convert-healthcare-service.js +60 -0
- package/dist/converters/convert-location.d.ts +4 -0
- package/dist/converters/convert-location.js +41 -3
- package/dist/converters/convert-organization.d.ts +2 -0
- package/dist/converters/convert-organization.js +20 -3
- package/dist/converters/convert-procedure.d.ts +2 -0
- package/dist/converters/convert-procedure.js +21 -0
- package/dist/converters/convert-regulated-authorization.d.ts +3 -0
- package/dist/converters/convert-regulated-authorization.js +34 -0
- package/dist/converters/convert-schedule-slot.d.ts +9 -0
- package/dist/converters/convert-schedule-slot.js +96 -0
- package/dist/converters/convert-shared.d.ts +10 -0
- package/dist/converters/convert-shared.js +19 -0
- package/dist/converters/index.d.ts +6 -0
- package/dist/converters/index.js +6 -0
- package/dist/converters/materialize-claims-first-resource.d.ts +16 -0
- package/dist/converters/materialize-claims-first-resource.js +61 -0
- package/dist/fhir-bundle.d.ts +21 -0
- package/dist/fhir-bundle.js +74 -0
- package/dist/group-r5.d.ts +12 -0
- package/dist/group-r5.js +20 -0
- package/dist/healthcare-service-specialty-map.d.ts +32 -0
- package/dist/healthcare-service-specialty-map.js +151 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/observation-claims.d.ts +3 -0
- package/dist/observation-claims.js +3 -0
- package/dist/vital-sign-observations.d.ts +75 -1
- package/dist/vital-sign-observations.js +225 -9
- package/docs/CLINICAL_TYPED_ACCESSOR_BOUNDARY.md +44 -0
- package/docs/COMMUNICATION_BUNDLE_CONTRACT.md +61 -0
- package/docs/HEALTHCARE_SERVICE_DEPARTMENT_CATALOG.md +190 -0
- package/docs/UHC_FHIR_UTILS_MIGRATION.md +20 -1
- package/docs/VITAL_SIGN_DAILY_DOCUMENT.md +79 -0
- package/package.json +5 -1
|
@@ -29,10 +29,45 @@ description: Complete or audit migrations, claim catalogs, converters, builders,
|
|
|
29
29
|
projection. Do not invent resource-specific scalar aliases for indexing.
|
|
30
30
|
- Keep Group membership and ResearchStudy associated parties descriptive;
|
|
31
31
|
neither is a Consent, SMART scope or authorization grant.
|
|
32
|
+
- Keep legal Organizations, department Organizations, HealthcareServices and
|
|
33
|
+
Locations distinct. A professional Group has one department managingEntity;
|
|
34
|
+
endpoint routing and access remain downstream policy.
|
|
35
|
+
- Build a department and all independently schedulable offered services with
|
|
36
|
+
`buildDepartmentServicesFlatGraph(...)`; require complete identifier and
|
|
37
|
+
coding tokens plus typed references, and persist one Organization plus one
|
|
38
|
+
claims-first HealthcareService row per service.
|
|
39
|
+
- Keep `Schedule` and `Slot` as independent claims-first resources. A Schedule
|
|
40
|
+
names its HealthcareService plus all independently required Location, Device
|
|
41
|
+
or professional actors; every Slot references its Schedule and derives
|
|
42
|
+
duration only from required start/end instants.
|
|
43
|
+
- Model rooms, booths, chairs and beds as child Locations using `partOf`.
|
|
44
|
+
Model equipment as Device, never as a synthetic Service or Place. R4
|
|
45
|
+
`Location.physicalType` and R5 `Location.form` project the same governed
|
|
46
|
+
version-neutral physical-form claim.
|
|
47
|
+
- Keep equipment calibration and public authorization separate. A calibration
|
|
48
|
+
is a Procedure focused on the Device, a DeviceMetric calibration state/time
|
|
49
|
+
and optional DocumentReference evidence. A public operating approval is an
|
|
50
|
+
R5 RegulatedAuthorization whose holder and regulator are distinct. Neither
|
|
51
|
+
record grants the submitter authority, and neither is collapsed into the
|
|
52
|
+
Schedule or a browser-authored availability flag.
|
|
53
|
+
- Treat minimal identity plus `resource.meta.claims` as the only canonical
|
|
54
|
+
persisted resource representation. R4/R5 materializers keep each department
|
|
55
|
+
and service id independent; the native result is an adapter output for
|
|
56
|
+
interop, hashing or credentials, never a parallel stored source of truth.
|
|
57
|
+
- Preserve terminology-map relationship evidence. Only a strict reviewed
|
|
58
|
+
equivalence may be selected automatically; wider, related, inexact and
|
|
59
|
+
unmatched mappings require terminology governance.
|
|
32
60
|
- Keep native R5 SubscriptionTopic/Subscription configuration and notification
|
|
33
61
|
Bundle parsing separate from the claims-first Communication being delivered.
|
|
34
62
|
This package owns their reusable types and validation, never HTTP delivery,
|
|
35
63
|
subscriber persistence, authentication or product notification policy.
|
|
64
|
+
- Validate and project the canonical outer `Bundle[type=batch]` ->
|
|
65
|
+
`Communication` -> inner `Bundle[type=document|batch|collection]` structure
|
|
66
|
+
here, but keep the cross-product high-level editor/reader in `sos-sdk-core`
|
|
67
|
+
and its data contracts in `sos-data-utils`. Neither replacement package may
|
|
68
|
+
import frozen `gdc-*`. Arbitrary files are DocumentReference entries in the
|
|
69
|
+
inner Bundle, never a second direct Communication attachment. Direct
|
|
70
|
+
flat-claim CRUD is an internal persistence boundary, not this wire format.
|
|
36
71
|
- Keep ImagingStudy searchable metadata, clinical references and protected
|
|
37
72
|
endpoint references in neutral flat claims. DICOM parsing, pixels and object
|
|
38
73
|
storage remain in a separately versioned imaging/storage package.
|
|
@@ -53,6 +53,17 @@ description: Implement or audit neutral FHIR Observation claim catalogs, vital-s
|
|
|
53
53
|
copy `Observation.*`, LOINC, UCUM or category strings into portals,
|
|
54
54
|
assistants, tests or snippets.
|
|
55
55
|
- Keep displays separate from `system|code` tokens and local-language text.
|
|
56
|
+
- For repeated vital signs, keep every raw reading as `registered`, require the
|
|
57
|
+
configured cadence before deriving a `preliminary` mean, and preserve the
|
|
58
|
+
source links through `Observation.derived-from`.
|
|
59
|
+
- Persist the EventTiming bucket through the canonical
|
|
60
|
+
`Observation.date-when` claim. Do not infer MORN/EVE from a server time zone.
|
|
61
|
+
- Build one preliminary Composition per explicit local date and IANA time
|
|
62
|
+
zone. Only authorized confirmation returns a final daily document with a
|
|
63
|
+
professional attester and final top-level Observations.
|
|
64
|
+
- Keep ledger hashing, individual-index writes, Digital Twin materialization
|
|
65
|
+
and research eligibility outside this neutral package. A final graph is data
|
|
66
|
+
state, not authorization for those side effects.
|
|
56
67
|
|
|
57
68
|
## Change with TDD
|
|
58
69
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.6.0 - 2026-09-29
|
|
6
|
+
|
|
7
|
+
- Add canonical two-level Communication Bundle validation tools recovered from
|
|
8
|
+
the frozen shared implementation: outer batch Bundle, one FHIR Bundle
|
|
9
|
+
attachment per Communication, and validated document/batch/collection inner
|
|
10
|
+
Bundles. Reject direct clinical or binary Communication payloads while
|
|
11
|
+
leaving high-level mutable editors to `sos-sdk-core`.
|
|
12
|
+
- Add a typed Appointment exchange projection that preserves start/end,
|
|
13
|
+
participants, slots and supporting information while keeping an ICS calendar
|
|
14
|
+
file as an independent DocumentReference flat-claim resource.
|
|
15
|
+
|
|
16
|
+
## 0.5.0 - 2026-09-29
|
|
17
|
+
|
|
18
|
+
- Add claims-first equipment compliance graphs: technical calibration as a
|
|
19
|
+
Device-focused Procedure plus DeviceMetric state and optional hashed
|
|
20
|
+
DocumentReference evidence, and public operating approval as a separate R5
|
|
21
|
+
RegulatedAuthorization. Keep these records independent from Schedule
|
|
22
|
+
availability and downstream role authorization.
|
|
23
|
+
|
|
24
|
+
- Add official R5 Schedule and Slot search-claim catalogs, claims-first
|
|
25
|
+
materializers and a service availability graph builder. Preserve independent
|
|
26
|
+
HealthcareService/Location actors and derive booking duration from each
|
|
27
|
+
Slot start/end interval.
|
|
28
|
+
- Add schedulable child Location construction and composite Schedule actors for
|
|
29
|
+
Location, Device and PractitionerRole capacity. Correct R5 Location
|
|
30
|
+
projection to use `form` instead of the removed R4 `physicalType` field.
|
|
31
|
+
- Add a strict claims-first department/service graph builder that emits a
|
|
32
|
+
department Organization and one independently schedulable HealthcareService
|
|
33
|
+
per offered service, with complete identifiers, coding tokens and Location
|
|
34
|
+
references. Add governed
|
|
35
|
+
R5 Organization description and HealthcareService comment projection without
|
|
36
|
+
presenting either as an R4 SearchParameter.
|
|
37
|
+
|
|
38
|
+
- Added strict claims-first R5 materialization for Organization,
|
|
39
|
+
HealthcareService and Location. Canonical `system|value` identifiers and
|
|
40
|
+
`system|code` codings now retain their systems, while nested native FHIR
|
|
41
|
+
beside `resource.meta.claims` is rejected.
|
|
42
|
+
|
|
43
|
+
- Document the ownership boundary for allergy reaction severity, medication
|
|
44
|
+
treatment period, immunization route/site and Observation reference-range
|
|
45
|
+
text across neutral FHIR projections, SOS multichannel schemas and typed SDK
|
|
46
|
+
Bundle editors.
|
|
47
|
+
|
|
48
|
+
- Add the complete official FHIR R4 HealthcareService search-claim catalog and
|
|
49
|
+
a lossless R4 converter that keeps provider departments and services distinct.
|
|
50
|
+
- Preserve Location WGS84 coordinates and require each professional service
|
|
51
|
+
Group to have one managing department Organization without granting access.
|
|
52
|
+
- Add the supplied 115-row SNOMED CT C80 to LOINC specialty candidate map;
|
|
53
|
+
only strict equivalence is eligible for automatic selection.
|
|
54
|
+
|
|
55
|
+
## 0.4.0 - 2026-09-27
|
|
56
|
+
|
|
57
|
+
- Add the neutral daily vital-sign document lifecycle: registered raw readings,
|
|
58
|
+
preliminary three-reading means, MORN/EVE EventTiming, one preliminary daily
|
|
59
|
+
Composition and explicit professional finalization.
|
|
60
|
+
- Preserve mean provenance through `Observation.derived-from`, validate the
|
|
61
|
+
local calendar day with an IANA time zone and project Composition status plus
|
|
62
|
+
section entry references at the FHIR R4 boundary.
|
|
63
|
+
- Add governed UCUM Fahrenheit and avoirdupois-pound input alongside Celsius
|
|
64
|
+
and kilograms without moving regional UI preferences into shared FHIR data.
|
|
65
|
+
|
|
3
66
|
## 0.3.7 - 2026-09-26
|
|
4
67
|
|
|
5
68
|
- Add claims-first FHIR contracts for Location-linked Encounter care journeys,
|
package/README.md
CHANGED
|
@@ -36,12 +36,37 @@ independent `DocumentReference`. Preliminary reports can be reviewed and
|
|
|
36
36
|
securely exchanged; ledger and research eligibility remain downstream policy
|
|
37
37
|
and normally require the confirmed final lifecycle state.
|
|
38
38
|
|
|
39
|
+
Provider catalogs keep legal Organizations, department Organizations,
|
|
40
|
+
HealthcareServices and Locations separate. A department uses
|
|
41
|
+
`Organization.part-of`; each offered service references its provider through
|
|
42
|
+
`HealthcareService.organization`, and may reference several geocoded
|
|
43
|
+
Locations. Professional Groups are bound to one department without turning
|
|
44
|
+
membership into authorization. See
|
|
45
|
+
[Organization departments and healthcare-service catalogs](docs/HEALTHCARE_SERVICE_DEPARTMENT_CATALOG.md).
|
|
46
|
+
|
|
47
|
+
Cross-participant data always uses an outer FHIR batch Bundle containing
|
|
48
|
+
Communication entries. Each Communication carries exactly one
|
|
49
|
+
`application/fhir+json` inner document, batch or collection Bundle; arbitrary
|
|
50
|
+
files are DocumentReference resources inside that Bundle. This package owns
|
|
51
|
+
strict parsing and projection tools; the shared high-level editor/reader belongs
|
|
52
|
+
to `sos-sdk-core`, with cross-sector contracts in `sos-data-utils`. See
|
|
53
|
+
[Communication Bundle contract](docs/COMMUNICATION_BUNDLE_CONTRACT.md).
|
|
54
|
+
|
|
55
|
+
Reservable equipment remains a `Device` actor of its service-specific
|
|
56
|
+
`Schedule`. Technical calibration is recorded as a `Procedure` focused on that
|
|
57
|
+
Device plus the current `DeviceMetric.calibration` state and an optional
|
|
58
|
+
`DocumentReference` evidence hash. A public operating approval is a separate
|
|
59
|
+
R5 `RegulatedAuthorization`; it is never inferred from calibration or from an
|
|
60
|
+
employee role.
|
|
61
|
+
|
|
39
62
|
## Public contract
|
|
40
63
|
|
|
41
64
|
The root export and stable subpaths provide:
|
|
42
65
|
|
|
43
66
|
- strict version-neutral FHIR datatypes, value/token helpers, Bundle traversal
|
|
44
67
|
and typed search parsing/building;
|
|
68
|
+
- the canonical Communication Bundle validation boundary and Appointment
|
|
69
|
+
exchange projection, including calendar DocumentReference evidence;
|
|
45
70
|
- complete flat-claim catalogs and claim helpers for the resource families
|
|
46
71
|
formerly scattered through `gdc-common-utils-ts`;
|
|
47
72
|
- explicit R4/R5 conversion adapters, including Consent and PractitionerRole;
|
|
@@ -72,6 +97,11 @@ Use `fhir-data-utils-ts/claims`, `/converters`, `/fhir-bundle`,
|
|
|
72
97
|
`/fhir-search-parameters`, `/clinical-terminology`, `/group-r5` or
|
|
73
98
|
`/research-study-r5` as appropriate.
|
|
74
99
|
|
|
100
|
+
The `/healthcare-service-specialty-map` subpath exposes the complete supplied
|
|
101
|
+
C80-to-LOINC candidate mapping. Only strict equivalence can be selected
|
|
102
|
+
automatically; wider, related, inexact and unmatched candidates remain review
|
|
103
|
+
evidence.
|
|
104
|
+
|
|
75
105
|
Repeatable `CodeableConcept.coding[]` identities are serialized in the owning
|
|
76
106
|
token claim as a comma-separated list of complete `system|code` tokens. The
|
|
77
107
|
first `*-display` remains source evidence for compatibility; authoritative
|
|
@@ -105,10 +135,24 @@ without inventing a new flat-claim family for each scale.
|
|
|
105
135
|
`Observation.user-selected` provenance flag. Consumers submit its `entries`
|
|
106
136
|
without reconstructing component claims or relationships.
|
|
107
137
|
|
|
138
|
+
Repeated morning/evening capture uses `buildRepeatedVitalSignAverage`: three
|
|
139
|
+
two-minute raw readings remain `registered`, while their derived arithmetic
|
|
140
|
+
mean is `preliminary`. `buildVitalSignDailyDocument` groups the day's top-level
|
|
141
|
+
Observations under one preliminary Composition; only
|
|
142
|
+
`finalizeVitalSignDailyDocument` returns a final Composition and final
|
|
143
|
+
top-level Observations with a professional attester. Ledger hashing, index
|
|
144
|
+
updates, Digital Twin and research eligibility remain owning-service policy.
|
|
145
|
+
See [Daily vital-sign document lifecycle](docs/VITAL_SIGN_DAILY_DOCUMENT.md).
|
|
146
|
+
|
|
108
147
|
See [the UHC FHIR utilities migration](docs/UHC_FHIR_UTILS_MIGRATION.md) for the
|
|
109
148
|
reuse/rewrite/retire inventory and the rule for moving frozen contracts without
|
|
110
149
|
changing any `gdc-*` repository.
|
|
111
150
|
|
|
151
|
+
See [Clinical typed accessor boundary](docs/CLINICAL_TYPED_ACCESSOR_BOUNDARY.md)
|
|
152
|
+
for the exact division between canonical clinical claims/converters here,
|
|
153
|
+
shared SOS multichannel schemas and the mutable Bundle getters/setters exposed
|
|
154
|
+
by runtime SDKs.
|
|
155
|
+
|
|
112
156
|
## Coding review for imported clinical text
|
|
113
157
|
|
|
114
158
|
DataConv, Vet and UHC share one neutral review contract from
|
|
@@ -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,32 @@
|
|
|
1
|
+
export type ClaimsFirstGraphResource = Readonly<{
|
|
2
|
+
resourceType: 'Organization' | 'HealthcareService';
|
|
3
|
+
id: string;
|
|
4
|
+
meta: Readonly<{
|
|
5
|
+
claims: Readonly<Record<string, string | readonly string[]>>;
|
|
6
|
+
}>;
|
|
7
|
+
}>;
|
|
8
|
+
export type DepartmentServiceInput = Readonly<{
|
|
9
|
+
serviceId: string;
|
|
10
|
+
serviceIdentifier: string;
|
|
11
|
+
name: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
serviceCategory?: readonly string[];
|
|
14
|
+
serviceType: readonly string[];
|
|
15
|
+
specialty?: readonly string[];
|
|
16
|
+
locationReferences: readonly string[];
|
|
17
|
+
}>;
|
|
18
|
+
export type DepartmentServicesFlatGraphInput = Readonly<{
|
|
19
|
+
departmentId: string;
|
|
20
|
+
departmentIdentifier: string;
|
|
21
|
+
parentOrganizationReference: string;
|
|
22
|
+
name: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
organizationType?: string;
|
|
25
|
+
services: readonly DepartmentServiceInput[];
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Builds one department Organization and one independent HealthcareService
|
|
29
|
+
* row per offered service. Schedule actors can therefore target a blood,
|
|
30
|
+
* plasma or other service independently even when they share a department.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildDepartmentServicesFlatGraph(input: DepartmentServicesFlatGraphInput): readonly ClaimsFirstGraphResource[];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { HealthcareServiceClaim } from '../claims/healthcare-service-claims.js';
|
|
2
|
+
import { OrganizationClaim } from '../claims/organization-claims.js';
|
|
3
|
+
const required = (value, error) => {
|
|
4
|
+
const normalized = String(value || '').trim();
|
|
5
|
+
if (!normalized)
|
|
6
|
+
throw new TypeError(error);
|
|
7
|
+
return normalized;
|
|
8
|
+
};
|
|
9
|
+
const resourceId = (value) => {
|
|
10
|
+
const normalized = required(value, 'department_service_id_required');
|
|
11
|
+
if (!/^[A-Za-z0-9.-]{1,64}$/.test(normalized))
|
|
12
|
+
throw new TypeError('department_service_id_invalid');
|
|
13
|
+
return normalized;
|
|
14
|
+
};
|
|
15
|
+
const reference = (value, prefix, error) => {
|
|
16
|
+
const normalized = required(value, error);
|
|
17
|
+
if (!normalized.startsWith(`${prefix}/`) || normalized.length === prefix.length + 1)
|
|
18
|
+
throw new TypeError(error);
|
|
19
|
+
return normalized;
|
|
20
|
+
};
|
|
21
|
+
const token = (value, error) => {
|
|
22
|
+
const normalized = required(value, error);
|
|
23
|
+
const separator = normalized.lastIndexOf('|');
|
|
24
|
+
if (separator < 1 || separator === normalized.length - 1)
|
|
25
|
+
throw new TypeError(error);
|
|
26
|
+
return normalized;
|
|
27
|
+
};
|
|
28
|
+
const tokenList = (values, error, requiredList = false) => {
|
|
29
|
+
const normalized = Object.freeze((values ?? []).map(value => token(value, error)));
|
|
30
|
+
if (requiredList && !normalized.length)
|
|
31
|
+
throw new TypeError(error);
|
|
32
|
+
return normalized.length ? normalized : undefined;
|
|
33
|
+
};
|
|
34
|
+
const referenceList = (values, prefix, error) => {
|
|
35
|
+
const normalized = Object.freeze(values.map(value => reference(value, prefix, error)));
|
|
36
|
+
if (!normalized.length)
|
|
37
|
+
throw new TypeError(error);
|
|
38
|
+
return normalized;
|
|
39
|
+
};
|
|
40
|
+
const compactClaims = (claims) => Object.freeze(Object.fromEntries(Object.entries(claims).filter((entry) => entry[1] !== undefined)));
|
|
41
|
+
/**
|
|
42
|
+
* Builds one department Organization and one independent HealthcareService
|
|
43
|
+
* row per offered service. Schedule actors can therefore target a blood,
|
|
44
|
+
* plasma or other service independently even when they share a department.
|
|
45
|
+
*/
|
|
46
|
+
export function buildDepartmentServicesFlatGraph(input) {
|
|
47
|
+
const departmentId = resourceId(input.departmentId);
|
|
48
|
+
const departmentIdentifier = token(input.departmentIdentifier, 'department_service_identifier_invalid');
|
|
49
|
+
const parentOrganizationReference = reference(input.parentOrganizationReference, 'Organization', 'department_service_parent_reference_invalid');
|
|
50
|
+
const name = required(input.name, 'department_service_name_required');
|
|
51
|
+
const description = input.description?.trim() || undefined;
|
|
52
|
+
const organizationType = input.organizationType
|
|
53
|
+
? token(input.organizationType, 'department_service_organization_type_invalid')
|
|
54
|
+
: undefined;
|
|
55
|
+
const departmentReference = `Organization/${departmentId}`;
|
|
56
|
+
if (!input.services.length)
|
|
57
|
+
throw new TypeError('department_services_required');
|
|
58
|
+
const serviceIds = new Set();
|
|
59
|
+
const services = input.services.map(service => {
|
|
60
|
+
const serviceId = resourceId(service.serviceId);
|
|
61
|
+
if (serviceIds.has(serviceId))
|
|
62
|
+
throw new TypeError('department_service_id_duplicate');
|
|
63
|
+
serviceIds.add(serviceId);
|
|
64
|
+
const serviceIdentifier = token(service.serviceIdentifier, 'department_service_identifier_invalid');
|
|
65
|
+
const serviceName = required(service.name, 'department_service_name_required');
|
|
66
|
+
const serviceDescription = service.description?.trim() || undefined;
|
|
67
|
+
const serviceCategory = tokenList(service.serviceCategory, 'department_service_category_invalid');
|
|
68
|
+
const serviceType = tokenList(service.serviceType, 'department_service_type_invalid', true);
|
|
69
|
+
const specialty = tokenList(service.specialty, 'department_service_specialty_invalid');
|
|
70
|
+
const locationReferences = referenceList(service.locationReferences, 'Location', 'department_service_location_reference_invalid');
|
|
71
|
+
return Object.freeze({
|
|
72
|
+
resourceType: 'HealthcareService',
|
|
73
|
+
id: serviceId,
|
|
74
|
+
meta: Object.freeze({ claims: compactClaims({
|
|
75
|
+
[HealthcareServiceClaim.Identifier]: serviceIdentifier,
|
|
76
|
+
[HealthcareServiceClaim.Active]: 'true',
|
|
77
|
+
[HealthcareServiceClaim.Organization]: departmentReference,
|
|
78
|
+
[HealthcareServiceClaim.ServiceCategory]: serviceCategory,
|
|
79
|
+
[HealthcareServiceClaim.ServiceType]: serviceType,
|
|
80
|
+
[HealthcareServiceClaim.Specialty]: specialty,
|
|
81
|
+
[HealthcareServiceClaim.Location]: locationReferences,
|
|
82
|
+
[HealthcareServiceClaim.Name]: serviceName,
|
|
83
|
+
[HealthcareServiceClaim.Comment]: serviceDescription,
|
|
84
|
+
}) }),
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
return Object.freeze([
|
|
88
|
+
Object.freeze({
|
|
89
|
+
resourceType: 'Organization',
|
|
90
|
+
id: departmentId,
|
|
91
|
+
meta: Object.freeze({ claims: compactClaims({
|
|
92
|
+
[OrganizationClaim.Identifier]: departmentIdentifier,
|
|
93
|
+
[OrganizationClaim.Active]: 'true',
|
|
94
|
+
[OrganizationClaim.Type]: organizationType,
|
|
95
|
+
[OrganizationClaim.Name]: name,
|
|
96
|
+
[OrganizationClaim.Description]: description,
|
|
97
|
+
[OrganizationClaim.PartOf]: parentOrganizationReference,
|
|
98
|
+
}) }),
|
|
99
|
+
}),
|
|
100
|
+
...services,
|
|
101
|
+
]);
|
|
102
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
type FlatResourceType = 'Procedure' | 'DeviceMetric' | 'DocumentReference' | 'RegulatedAuthorization';
|
|
2
|
+
export type DeviceComplianceFlatResource = Readonly<{
|
|
3
|
+
resourceType: FlatResourceType;
|
|
4
|
+
id: string;
|
|
5
|
+
meta: Readonly<{
|
|
6
|
+
claims: Readonly<Record<string, string>>;
|
|
7
|
+
}>;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* Builds the technical calibration event separately from the current metric state and its evidence.
|
|
11
|
+
* Performer references are descriptive; product authorization decides which credentialed actor may submit.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildDeviceCalibrationFlatGraph(input: Readonly<{
|
|
14
|
+
procedureId: string;
|
|
15
|
+
procedureIdentifier: string;
|
|
16
|
+
procedureCode: string;
|
|
17
|
+
deviceReference: string;
|
|
18
|
+
metricId: string;
|
|
19
|
+
metricIdentifier: string;
|
|
20
|
+
metricType: string;
|
|
21
|
+
metricUnit?: string;
|
|
22
|
+
calibrationType: 'unspecified' | 'offset' | 'gain' | 'two-point';
|
|
23
|
+
calibrationState: 'not-calibrated' | 'calibration-required' | 'calibrated' | 'unspecified';
|
|
24
|
+
calibrationTime: string;
|
|
25
|
+
performerReferences: readonly string[];
|
|
26
|
+
locationReference?: string;
|
|
27
|
+
evidence?: Readonly<{
|
|
28
|
+
documentId: string;
|
|
29
|
+
documentIdentifier: string;
|
|
30
|
+
contentHash: string;
|
|
31
|
+
contentType: string;
|
|
32
|
+
}>;
|
|
33
|
+
}>): readonly DeviceComplianceFlatResource[];
|
|
34
|
+
/** Builds a public-authority operating authorization without conflating it with calibration. */
|
|
35
|
+
export declare function buildDeviceOperatingAuthorizationFlatResource(input: Readonly<{
|
|
36
|
+
authorizationId: string;
|
|
37
|
+
identifier: string;
|
|
38
|
+
subjectReference: string;
|
|
39
|
+
holderReference: string;
|
|
40
|
+
regulatorReference: string;
|
|
41
|
+
type: string;
|
|
42
|
+
status: string;
|
|
43
|
+
statusDate: string;
|
|
44
|
+
validityPeriod: Readonly<{
|
|
45
|
+
start: string;
|
|
46
|
+
end: string;
|
|
47
|
+
}>;
|
|
48
|
+
region: string;
|
|
49
|
+
basis: string;
|
|
50
|
+
}>): DeviceComplianceFlatResource;
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { DeviceMetricClaim } from '../claims/device-metric-claims.js';
|
|
2
|
+
import { DocumentReferenceClaim } from '../claims/document-reference-claims.js';
|
|
3
|
+
import { ProcedureClaim } from '../claims/procedure-claims.js';
|
|
4
|
+
import { RegulatedAuthorizationClaim } from '../claims/regulated-authorization-claims.js';
|
|
5
|
+
const required = (value, error) => {
|
|
6
|
+
const normalized = String(value || '').trim();
|
|
7
|
+
if (!normalized)
|
|
8
|
+
throw new TypeError(error);
|
|
9
|
+
return normalized;
|
|
10
|
+
};
|
|
11
|
+
const id = (value) => {
|
|
12
|
+
const normalized = required(value, 'device_compliance_id_required');
|
|
13
|
+
if (!/^[A-Za-z0-9.-]{1,64}$/.test(normalized))
|
|
14
|
+
throw new TypeError('device_compliance_id_invalid');
|
|
15
|
+
return normalized;
|
|
16
|
+
};
|
|
17
|
+
const reference = (value, resourceType, error) => {
|
|
18
|
+
const normalized = required(value, error);
|
|
19
|
+
if (!new RegExp(`^${resourceType}/[A-Za-z0-9.-]+$`).test(normalized))
|
|
20
|
+
throw new TypeError(error);
|
|
21
|
+
return normalized;
|
|
22
|
+
};
|
|
23
|
+
const actorReference = (value) => {
|
|
24
|
+
const normalized = required(value, 'calibration_performer_reference_invalid');
|
|
25
|
+
if (!/^(?:Practitioner|PractitionerRole|Organization|HealthcareService|Device)\/[A-Za-z0-9.-]+$/.test(normalized)) {
|
|
26
|
+
throw new TypeError('calibration_performer_reference_invalid');
|
|
27
|
+
}
|
|
28
|
+
return normalized;
|
|
29
|
+
};
|
|
30
|
+
const token = (value, error) => {
|
|
31
|
+
const normalized = required(value, error);
|
|
32
|
+
const separator = normalized.lastIndexOf('|');
|
|
33
|
+
if (separator < 1 || separator === normalized.length - 1)
|
|
34
|
+
throw new TypeError(error);
|
|
35
|
+
return normalized;
|
|
36
|
+
};
|
|
37
|
+
const instant = (value, error) => {
|
|
38
|
+
const normalized = required(value, error);
|
|
39
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(normalized))
|
|
40
|
+
throw new TypeError(error);
|
|
41
|
+
return normalized;
|
|
42
|
+
};
|
|
43
|
+
const resource = (resourceType, resourceId, claims) => Object.freeze({
|
|
44
|
+
resourceType,
|
|
45
|
+
id: id(resourceId),
|
|
46
|
+
meta: Object.freeze({ claims: Object.freeze(claims) }),
|
|
47
|
+
});
|
|
48
|
+
/**
|
|
49
|
+
* Builds the technical calibration event separately from the current metric state and its evidence.
|
|
50
|
+
* Performer references are descriptive; product authorization decides which credentialed actor may submit.
|
|
51
|
+
*/
|
|
52
|
+
export function buildDeviceCalibrationFlatGraph(input) {
|
|
53
|
+
const deviceReference = reference(input.deviceReference, 'Device', 'device_reference_invalid');
|
|
54
|
+
const performers = [...new Set(input.performerReferences.map(actorReference))];
|
|
55
|
+
if (!performers.length)
|
|
56
|
+
throw new TypeError('calibration_performer_required');
|
|
57
|
+
const calibrationTime = instant(input.calibrationTime, 'calibration_time_invalid');
|
|
58
|
+
const reportReference = input.evidence ? `DocumentReference/${id(input.evidence.documentId)}` : undefined;
|
|
59
|
+
const procedure = resource('Procedure', input.procedureId, {
|
|
60
|
+
[ProcedureClaim.Identifier]: required(input.procedureIdentifier, 'procedure_identifier_required'),
|
|
61
|
+
[ProcedureClaim.Status]: 'completed',
|
|
62
|
+
[ProcedureClaim.Code]: token(input.procedureCode, 'procedure_code_invalid'),
|
|
63
|
+
[ProcedureClaim.Subject]: deviceReference,
|
|
64
|
+
[ProcedureClaim.FocalDevice]: deviceReference,
|
|
65
|
+
[ProcedureClaim.Date]: calibrationTime,
|
|
66
|
+
[ProcedureClaim.Performer]: performers.join(','),
|
|
67
|
+
...(input.locationReference ? { [ProcedureClaim.Location]: reference(input.locationReference, 'Location', 'calibration_location_reference_invalid') } : {}),
|
|
68
|
+
...(reportReference ? { [ProcedureClaim.Report]: reportReference } : {}),
|
|
69
|
+
});
|
|
70
|
+
const metric = resource('DeviceMetric', input.metricId, {
|
|
71
|
+
[DeviceMetricClaim.Identifier]: required(input.metricIdentifier, 'metric_identifier_required'),
|
|
72
|
+
[DeviceMetricClaim.Type]: token(input.metricType, 'metric_type_invalid'),
|
|
73
|
+
...(input.metricUnit ? { [DeviceMetricClaim.Unit]: token(input.metricUnit, 'metric_unit_invalid') } : {}),
|
|
74
|
+
[DeviceMetricClaim.Device]: deviceReference,
|
|
75
|
+
[DeviceMetricClaim.Category]: 'measurement',
|
|
76
|
+
[DeviceMetricClaim.CalibrationType]: input.calibrationType,
|
|
77
|
+
[DeviceMetricClaim.CalibrationState]: input.calibrationState,
|
|
78
|
+
[DeviceMetricClaim.CalibrationTime]: calibrationTime,
|
|
79
|
+
});
|
|
80
|
+
const evidence = input.evidence
|
|
81
|
+
? [resource('DocumentReference', input.evidence.documentId, {
|
|
82
|
+
[DocumentReferenceClaim.Identifier]: required(input.evidence.documentIdentifier, 'calibration_evidence_identifier_required'),
|
|
83
|
+
[DocumentReferenceClaim.Subject]: deviceReference,
|
|
84
|
+
[DocumentReferenceClaim.Context]: `Procedure/${procedure.id}`,
|
|
85
|
+
[DocumentReferenceClaim.ContentHash]: required(input.evidence.contentHash, 'calibration_evidence_hash_required'),
|
|
86
|
+
[DocumentReferenceClaim.ContentType]: required(input.evidence.contentType, 'calibration_evidence_content_type_required'),
|
|
87
|
+
[DocumentReferenceClaim.Date]: calibrationTime,
|
|
88
|
+
})]
|
|
89
|
+
: [];
|
|
90
|
+
return Object.freeze([procedure, metric, ...evidence]);
|
|
91
|
+
}
|
|
92
|
+
/** Builds a public-authority operating authorization without conflating it with calibration. */
|
|
93
|
+
export function buildDeviceOperatingAuthorizationFlatResource(input) {
|
|
94
|
+
const start = required(input.validityPeriod.start, 'authorization_validity_invalid');
|
|
95
|
+
const end = required(input.validityPeriod.end, 'authorization_validity_invalid');
|
|
96
|
+
if (Date.parse(end) < Date.parse(start))
|
|
97
|
+
throw new TypeError('authorization_validity_invalid');
|
|
98
|
+
const subjectReference = required(input.subjectReference, 'authorization_subject_reference_invalid');
|
|
99
|
+
if (!/^(?:DeviceDefinition|Organization|Location|ActivityDefinition|PlanDefinition)\/[A-Za-z0-9.-]+$/.test(subjectReference)) {
|
|
100
|
+
throw new TypeError('authorization_subject_reference_invalid');
|
|
101
|
+
}
|
|
102
|
+
return resource('RegulatedAuthorization', input.authorizationId, {
|
|
103
|
+
[RegulatedAuthorizationClaim.Identifier]: required(input.identifier, 'authorization_identifier_required'),
|
|
104
|
+
[RegulatedAuthorizationClaim.Subject]: subjectReference,
|
|
105
|
+
[RegulatedAuthorizationClaim.Type]: token(input.type, 'authorization_type_invalid'),
|
|
106
|
+
[RegulatedAuthorizationClaim.Region]: token(input.region, 'authorization_region_invalid'),
|
|
107
|
+
[RegulatedAuthorizationClaim.Status]: required(input.status, 'authorization_status_required'),
|
|
108
|
+
[RegulatedAuthorizationClaim.StatusDate]: instant(input.statusDate, 'authorization_status_date_invalid'),
|
|
109
|
+
[RegulatedAuthorizationClaim.ValidityStart]: start,
|
|
110
|
+
[RegulatedAuthorizationClaim.ValidityEnd]: end,
|
|
111
|
+
[RegulatedAuthorizationClaim.Basis]: token(input.basis, 'authorization_basis_invalid'),
|
|
112
|
+
[RegulatedAuthorizationClaim.Holder]: reference(input.holderReference, 'Organization', 'authorization_holder_reference_invalid'),
|
|
113
|
+
[RegulatedAuthorizationClaim.Regulator]: reference(input.regulatorReference, 'Organization', 'authorization_regulator_reference_invalid'),
|
|
114
|
+
});
|
|
115
|
+
}
|
package/dist/builders/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export * from './diagnostic-report-graph.js';
|
|
2
|
+
export * from './department-service-graph.js';
|
|
3
|
+
export * from './service-schedule-graph.js';
|
|
4
|
+
export * from './device-compliance-graph.js';
|
|
2
5
|
export * from './immunization-graph.js';
|
|
3
6
|
export * from './immunization-reminders.js';
|