gdc-common-utils-ts 2.9.9 → 2.9.11
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/README.md +20 -3
- package/dist/constants/fhir-resource-types.d.ts +3 -0
- package/dist/constants/fhir-resource-types.js +1 -0
- package/dist/convert/convert-consent.d.ts +6 -0
- package/dist/convert/convert-consent.js +146 -50
- package/dist/examples/consent-projection.d.ts +28 -0
- package/dist/examples/consent-projection.js +29 -0
- package/dist/examples/index.d.ts +1 -0
- package/dist/examples/index.js +1 -0
- package/dist/models/consent-rule.d.ts +20 -0
- package/dist/models/consent-rule.js +7 -0
- package/dist/utils/fhir-ips-creator-identity.d.ts +14 -4
- package/dist/utils/fhir-ips-creator-identity.js +15 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -283,13 +283,23 @@ blockchain asset.
|
|
|
283
283
|
|
|
284
284
|
### Convert
|
|
285
285
|
|
|
286
|
-
The `convert` export exposes resource-specific FHIR
|
|
286
|
+
The `convert` export exposes resource-specific FHIR conversion helpers such as:
|
|
287
287
|
|
|
288
288
|
- `medicationStatementFlatToFhirR4`
|
|
289
289
|
- `medicationStatementFhirR4ToFlat`
|
|
290
290
|
- `observationFromFlatToFhirR4`
|
|
291
291
|
- `documentReferenceFlatToFhirR4`
|
|
292
292
|
- `compositionFlatToFhirR4`
|
|
293
|
+
- `consentFlatToFhirR4` / `consentFhirR4ToFlat`
|
|
294
|
+
- `consentFlatToFhirR5` / `consentFhirR5ToFlat`
|
|
295
|
+
|
|
296
|
+
Consent claims are version-neutral. Consent validity, data validity, grantor,
|
|
297
|
+
grantee, manager, enforcer, actor role, action, purpose, resource/content codes
|
|
298
|
+
and an optional source attachment stay in `resource.meta.claims`. Explicit R4
|
|
299
|
+
or R5 export maps only concepts supported by that version. For example, R5
|
|
300
|
+
exports `Consent.controller`, while R4 does not invent an equivalent field.
|
|
301
|
+
Computable policy content such as ODRL remains in `sourceAttachment`; its
|
|
302
|
+
profile must validate semantic parity before projection.
|
|
293
303
|
|
|
294
304
|
Example:
|
|
295
305
|
|
|
@@ -554,8 +564,12 @@ The canonical API contract should live in JSDoc on exported code. The README act
|
|
|
554
564
|
the document author; a registered PractitionerRole or RelatedPerson is the
|
|
555
565
|
attester, including for content entered by that authenticated person.
|
|
556
566
|
- [`normalizeClinicalCreatorBinding(...)`](src/utils/fhir-ips-creator-identity.ts)
|
|
557
|
-
- Accept stable
|
|
558
|
-
add canonical UUID URNs and HL7/ISCO coding
|
|
567
|
+
- Accept stable person, assignment and owner UUIDs plus governed bare role
|
|
568
|
+
codes at the BFF boundary, then add canonical UUID URNs and HL7/ISCO coding
|
|
569
|
+
systems before transport. Use `assignmentIdentifier` for the registered
|
|
570
|
+
`RelatedPerson`/`PractitionerRole`; the deprecated wire property
|
|
571
|
+
`authorIdentifier` names that assignment and does not decide
|
|
572
|
+
`Composition.author`.
|
|
559
573
|
- [`src/examples/frontend-session.ts`](src/examples/frontend-session.ts)
|
|
560
574
|
- Frontend profile/session bootstrap examples.
|
|
561
575
|
- [`src/examples/lifecycle.ts`](src/examples/lifecycle.ts)
|
|
@@ -673,6 +687,9 @@ when the active runtime variable is really the subject identifier.
|
|
|
673
687
|
- [`__tests__/utils-consent-claim-helpers.test.ts`](__tests__/utils-consent-claim-helpers.test.ts)
|
|
674
688
|
|
|
675
689
|
These helpers are the shared base for consent claim construction across GW and SDKs.
|
|
690
|
+
The canonical claim catalogue also distinguishes `Consent.period-*` (the
|
|
691
|
+
Consent's effective period) from `Consent.data-period-*` (the period of the
|
|
692
|
+
governed data).
|
|
676
693
|
|
|
677
694
|
### Public module surfaces
|
|
678
695
|
|
|
@@ -41,6 +41,7 @@ export declare const ResourceTypesFhirR4: Readonly<{
|
|
|
41
41
|
readonly PractitionerRole: "PractitionerRole";
|
|
42
42
|
readonly Procedure: "Procedure";
|
|
43
43
|
readonly RelatedPerson: "RelatedPerson";
|
|
44
|
+
readonly ResearchStudy: "ResearchStudy";
|
|
44
45
|
readonly ResearchSubject: "ResearchSubject";
|
|
45
46
|
readonly Specimen: "Specimen";
|
|
46
47
|
}>;
|
|
@@ -89,6 +90,7 @@ export declare const FhirResourceTypes: Readonly<{
|
|
|
89
90
|
readonly PractitionerRole: "PractitionerRole";
|
|
90
91
|
readonly Procedure: "Procedure";
|
|
91
92
|
readonly RelatedPerson: "RelatedPerson";
|
|
93
|
+
readonly ResearchStudy: "ResearchStudy";
|
|
92
94
|
readonly ResearchSubject: "ResearchSubject";
|
|
93
95
|
readonly Specimen: "Specimen";
|
|
94
96
|
}>;
|
|
@@ -131,6 +133,7 @@ export declare const ResourceTypesFhirR5: Readonly<{
|
|
|
131
133
|
readonly PractitionerRole: "PractitionerRole";
|
|
132
134
|
readonly Procedure: "Procedure";
|
|
133
135
|
readonly RelatedPerson: "RelatedPerson";
|
|
136
|
+
readonly ResearchStudy: "ResearchStudy";
|
|
134
137
|
readonly ResearchSubject: "ResearchSubject";
|
|
135
138
|
readonly Specimen: "Specimen";
|
|
136
139
|
}>;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
import type { FhirResource, FlatClaims } from './convert-shared';
|
|
2
|
+
/** Converts native FHIR R4 Consent into the canonical flat claims contract. */
|
|
2
3
|
export declare function consentFhirR4ToFlat(resource: FhirResource, context?: string): FlatClaims;
|
|
4
|
+
/** Projects canonical Consent claims to native FHIR R4. ODRL stays in sourceAttachment. */
|
|
3
5
|
export declare function consentFlatToFhirR4(claims: FlatClaims): FhirResource;
|
|
6
|
+
/** Converts native FHIR R5 Consent into the same version-neutral flat claims contract. */
|
|
7
|
+
export declare function consentFhirR5ToFlat(resource: FhirResource, context?: string): FlatClaims;
|
|
8
|
+
/** Projects canonical Consent claims to native FHIR R5. ODRL stays in sourceAttachment. */
|
|
9
|
+
export declare function consentFlatToFhirR5(claims: FlatClaims): FhirResource;
|
|
@@ -1,78 +1,174 @@
|
|
|
1
1
|
// Copyright 2026 Conéctate Soluciones y Aplicaciones SL under the Apache License, Version 2.0.
|
|
2
2
|
import { ClaimConsent } from '../models/consent-rule.js';
|
|
3
|
-
import {
|
|
3
|
+
import { ResourceTypesFhirR4 } from '../constants/fhir-resource-types.js';
|
|
4
|
+
import { codingFromValue, codingToValue, referenceToValue } from './convert-shared.js';
|
|
5
|
+
const firstReference = (value) => referenceToValue(value?.[0]);
|
|
6
|
+
const conceptFromClaim = (value, display) => {
|
|
7
|
+
const coding = codingFromValue(value)?.map((item) => ({ ...item, ...(display ? { display } : {}) }));
|
|
8
|
+
return coding ? [{ coding }] : undefined;
|
|
9
|
+
};
|
|
10
|
+
const referencesFromClaim = (value) => value ? value.split(',').filter(Boolean).map((reference) => ({ reference })) : undefined;
|
|
11
|
+
const codingsFromClaim = (value) => {
|
|
12
|
+
const codings = value?.split(',').filter(Boolean).flatMap((token) => codingFromValue(token) || []);
|
|
13
|
+
return codings?.length ? codings : undefined;
|
|
14
|
+
};
|
|
15
|
+
const codingsToClaim = (value) => {
|
|
16
|
+
const tokens = (value || []).map((coding) => codingToValue(coding)).filter(Boolean);
|
|
17
|
+
return tokens.length ? tokens.join(',') : undefined;
|
|
18
|
+
};
|
|
19
|
+
const conceptsToClaim = (value) => {
|
|
20
|
+
const tokens = (value || []).flatMap((concept) => concept.coding || []).map((coding) => codingToValue(coding)).filter(Boolean);
|
|
21
|
+
return tokens.length ? tokens.join(',') : undefined;
|
|
22
|
+
};
|
|
23
|
+
const conceptsFromClaim = (value, display) => codingsFromClaim(value)?.map((coding) => ({ coding: [{ ...coding, ...(display ? { display } : {}) }] }));
|
|
24
|
+
const conceptWithCodingsFromClaim = (value) => {
|
|
25
|
+
const coding = codingsFromClaim(value);
|
|
26
|
+
return coding ? { coding } : undefined;
|
|
27
|
+
};
|
|
28
|
+
const attachmentFromClaims = (claims) => claims[ClaimConsent.attachmentContentType] || claims[ClaimConsent.attachmentData]
|
|
29
|
+
? { contentType: claims[ClaimConsent.attachmentContentType], data: claims[ClaimConsent.attachmentData] }
|
|
30
|
+
: undefined;
|
|
31
|
+
const periodFromClaims = (claims, start, end) => claims[start] || claims[end] ? { start: claims[start], end: claims[end] } : undefined;
|
|
32
|
+
/** Converts native FHIR R4 Consent into the canonical flat claims contract. */
|
|
4
33
|
export function consentFhirR4ToFlat(resource, context = 'org.hl7.fhir.api') {
|
|
5
|
-
const
|
|
34
|
+
const category = resource.category?.[0];
|
|
6
35
|
const provision = resource.provision;
|
|
36
|
+
const attachment = resource.sourceAttachment;
|
|
7
37
|
const scope = resource.scope;
|
|
8
38
|
const policyRule = resource.policyRule;
|
|
9
39
|
return {
|
|
10
40
|
'@context': context,
|
|
11
|
-
[ClaimConsent.identifier]: resource.identifier?.[0]?.value
|
|
12
|
-
|| resource.id,
|
|
41
|
+
[ClaimConsent.identifier]: resource.identifier?.[0]?.value || resource.id,
|
|
13
42
|
[ClaimConsent.status]: resource.status,
|
|
14
43
|
[ClaimConsent.subject]: referenceToValue(resource.patient),
|
|
15
44
|
[ClaimConsent.patient]: referenceToValue(resource.patient),
|
|
16
45
|
[ClaimConsent.date]: resource.dateTime,
|
|
17
46
|
[ClaimConsent.decision]: provision?.type,
|
|
18
|
-
[ClaimConsent.action]: codingToValue(provision?.action?.[0]?.coding?.[0]),
|
|
19
47
|
[ClaimConsent.periodStart]: provision?.period?.start,
|
|
20
48
|
[ClaimConsent.periodEnd]: provision?.period?.end,
|
|
21
|
-
[ClaimConsent.
|
|
49
|
+
[ClaimConsent.dataPeriodStart]: provision?.dataPeriod?.start,
|
|
50
|
+
[ClaimConsent.dataPeriodEnd]: provision?.dataPeriod?.end,
|
|
51
|
+
[ClaimConsent.grantor]: firstReference(resource.performer),
|
|
52
|
+
[ClaimConsent.grantee]: referenceToValue(provision?.actor?.[0]?.reference),
|
|
53
|
+
[ClaimConsent.manager]: firstReference(resource.organization),
|
|
54
|
+
[ClaimConsent.actorIdentifier]: referenceToValue(provision?.actor?.[0]?.reference),
|
|
55
|
+
[ClaimConsent.actorRole]: codingsToClaim(provision?.actor?.[0]?.role?.coding),
|
|
56
|
+
[ClaimConsent.action]: conceptsToClaim(provision?.action),
|
|
57
|
+
[ClaimConsent.purpose]: codingsToClaim(provision?.purpose),
|
|
58
|
+
[ClaimConsent.securityLabel]: codingsToClaim(provision?.securityLabel),
|
|
59
|
+
[ClaimConsent.resourceType]: codingsToClaim(provision?.class),
|
|
60
|
+
[ClaimConsent.provisionCode]: conceptsToClaim(provision?.code),
|
|
61
|
+
[ClaimConsent.provisionCodeDisplay]: provision?.code?.[0]?.coding?.[0]?.display,
|
|
22
62
|
[ClaimConsent.sourceReference]: referenceToValue(resource.sourceReference),
|
|
23
|
-
[ClaimConsent.
|
|
24
|
-
[ClaimConsent.
|
|
25
|
-
[ClaimConsent.
|
|
63
|
+
[ClaimConsent.attachmentContentType]: attachment?.contentType,
|
|
64
|
+
[ClaimConsent.attachmentData]: attachment?.data,
|
|
65
|
+
[ClaimConsent.category]: codingToValue(category?.coding?.[0]),
|
|
66
|
+
[ClaimConsent.categoryText]: category?.text,
|
|
67
|
+
[ClaimConsent.categoryDisplay]: category?.coding?.[0]?.display,
|
|
26
68
|
[ClaimConsent.scope]: codingToValue(scope?.coding?.[0]),
|
|
27
69
|
[ClaimConsent.scopeDisplay]: scope?.coding?.[0]?.display,
|
|
28
70
|
[ClaimConsent.policyRule]: codingToValue(policyRule?.coding?.[0]),
|
|
29
|
-
[ClaimConsent.provisionCode]: codingToValue(provision?.code?.[0]?.coding?.[0]),
|
|
30
|
-
[ClaimConsent.provisionCodeDisplay]: provision?.code?.[0]?.coding?.[0]?.display,
|
|
31
71
|
};
|
|
32
72
|
}
|
|
73
|
+
/** Projects canonical Consent claims to native FHIR R4. ODRL stays in sourceAttachment. */
|
|
33
74
|
export function consentFlatToFhirR4(claims) {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
: {}),
|
|
39
|
-
}));
|
|
40
|
-
const actionCoding = codingFromValue(claims[ClaimConsent.action]);
|
|
41
|
-
const purposeCoding = codingFromValue(claims[ClaimConsent.purpose]);
|
|
42
|
-
const scopeCoding = codingFromValue(claims[ClaimConsent.scope])?.map((item) => ({ ...item, ...(claims[ClaimConsent.scopeDisplay] ? { display: claims[ClaimConsent.scopeDisplay] } : {}) }));
|
|
43
|
-
const provisionCodeCoding = codingFromValue(claims[ClaimConsent.provisionCode])?.map((item) => ({ ...item, ...(claims[ClaimConsent.provisionCodeDisplay] ? { display: claims[ClaimConsent.provisionCodeDisplay] } : {}) }));
|
|
75
|
+
const actorRole = conceptWithCodingsFromClaim(claims[ClaimConsent.actorRole]);
|
|
76
|
+
const actorReference = claims[ClaimConsent.grantee] || claims[ClaimConsent.actorIdentifier];
|
|
77
|
+
const provisionCode = conceptsFromClaim(claims[ClaimConsent.provisionCode], claims[ClaimConsent.provisionCodeDisplay]);
|
|
78
|
+
const attachment = attachmentFromClaims(claims);
|
|
44
79
|
return {
|
|
45
|
-
resourceType:
|
|
80
|
+
resourceType: ResourceTypesFhirR4.Consent,
|
|
46
81
|
identifier: claims[ClaimConsent.identifier] ? [{ value: claims[ClaimConsent.identifier] }] : undefined,
|
|
47
82
|
status: claims[ClaimConsent.status],
|
|
48
|
-
scope:
|
|
49
|
-
|
|
50
|
-
patient: claims[ClaimConsent.subject] || claims[ClaimConsent.patient]
|
|
51
|
-
? { reference: claims[ClaimConsent.subject] || claims[ClaimConsent.patient] }
|
|
52
|
-
: undefined,
|
|
83
|
+
scope: conceptFromClaim(claims[ClaimConsent.scope], claims[ClaimConsent.scopeDisplay])?.[0],
|
|
84
|
+
category: conceptFromClaim(claims[ClaimConsent.category], claims[ClaimConsent.categoryDisplay]),
|
|
85
|
+
patient: claims[ClaimConsent.subject] || claims[ClaimConsent.patient] ? { reference: claims[ClaimConsent.subject] || claims[ClaimConsent.patient] } : undefined,
|
|
53
86
|
dateTime: claims[ClaimConsent.date],
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
performer: referencesFromClaim(claims[ClaimConsent.grantor]),
|
|
88
|
+
organization: referencesFromClaim(claims[ClaimConsent.manager]),
|
|
89
|
+
sourceAttachment: attachment,
|
|
90
|
+
sourceReference: claims[ClaimConsent.sourceReference] ? { reference: claims[ClaimConsent.sourceReference] } : undefined,
|
|
91
|
+
policyRule: conceptFromClaim(claims[ClaimConsent.policyRule])?.[0],
|
|
92
|
+
provision: {
|
|
93
|
+
type: claims[ClaimConsent.decision],
|
|
94
|
+
period: periodFromClaims(claims, ClaimConsent.periodStart, ClaimConsent.periodEnd),
|
|
95
|
+
actor: actorReference ? [{ role: actorRole, reference: { reference: actorReference } }] : undefined,
|
|
96
|
+
action: conceptsFromClaim(claims[ClaimConsent.action]),
|
|
97
|
+
purpose: codingsFromClaim(claims[ClaimConsent.purpose]),
|
|
98
|
+
securityLabel: codingsFromClaim(claims[ClaimConsent.securityLabel]),
|
|
99
|
+
class: codingsFromClaim(claims[ClaimConsent.resourceType]),
|
|
100
|
+
code: provisionCode,
|
|
101
|
+
dataPeriod: periodFromClaims(claims, ClaimConsent.dataPeriodStart, ClaimConsent.dataPeriodEnd),
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** Converts native FHIR R5 Consent into the same version-neutral flat claims contract. */
|
|
106
|
+
export function consentFhirR5ToFlat(resource, context = 'org.hl7.fhir.api') {
|
|
107
|
+
const categories = resource.category;
|
|
108
|
+
const scopeConcept = categories?.find((item) => item.coding?.[0]?.system?.includes('consentscope'));
|
|
109
|
+
const categoryConcept = categories?.find((item) => item !== scopeConcept);
|
|
110
|
+
const provision = resource.provision?.[0];
|
|
111
|
+
const attachment = resource.sourceAttachment?.[0];
|
|
112
|
+
return {
|
|
113
|
+
'@context': context,
|
|
114
|
+
[ClaimConsent.identifier]: resource.identifier?.[0]?.value || resource.id,
|
|
115
|
+
[ClaimConsent.status]: resource.status,
|
|
116
|
+
[ClaimConsent.subject]: referenceToValue(resource.subject),
|
|
117
|
+
[ClaimConsent.date]: resource.date,
|
|
118
|
+
[ClaimConsent.decision]: resource.decision,
|
|
119
|
+
[ClaimConsent.periodStart]: resource.period?.start,
|
|
120
|
+
[ClaimConsent.periodEnd]: resource.period?.end,
|
|
121
|
+
[ClaimConsent.grantor]: firstReference(resource.grantor),
|
|
122
|
+
[ClaimConsent.grantee]: firstReference(resource.grantee),
|
|
123
|
+
[ClaimConsent.manager]: firstReference(resource.manager),
|
|
124
|
+
[ClaimConsent.controller]: firstReference(resource.controller),
|
|
125
|
+
[ClaimConsent.actorIdentifier]: referenceToValue(provision?.actor?.[0]?.reference) || firstReference(resource.grantee),
|
|
126
|
+
[ClaimConsent.actorRole]: codingsToClaim(provision?.actor?.[0]?.role?.coding),
|
|
127
|
+
[ClaimConsent.action]: conceptsToClaim(provision?.action),
|
|
128
|
+
[ClaimConsent.purpose]: codingsToClaim(provision?.purpose),
|
|
129
|
+
[ClaimConsent.securityLabel]: codingsToClaim(provision?.securityLabel),
|
|
130
|
+
[ClaimConsent.documentType]: codingsToClaim(provision?.documentType),
|
|
131
|
+
[ClaimConsent.resourceType]: codingsToClaim(provision?.resourceType),
|
|
132
|
+
[ClaimConsent.provisionCode]: conceptsToClaim(provision?.code),
|
|
133
|
+
[ClaimConsent.dataPeriodStart]: provision?.dataPeriod?.start,
|
|
134
|
+
[ClaimConsent.dataPeriodEnd]: provision?.dataPeriod?.end,
|
|
135
|
+
[ClaimConsent.attachmentContentType]: attachment?.contentType,
|
|
136
|
+
[ClaimConsent.attachmentData]: attachment?.data,
|
|
137
|
+
[ClaimConsent.sourceReference]: firstReference(resource.sourceReference),
|
|
138
|
+
[ClaimConsent.scope]: codingToValue(scopeConcept?.coding?.[0]),
|
|
139
|
+
[ClaimConsent.category]: codingToValue(categoryConcept?.coding?.[0] || categories?.[0]?.coding?.[0]),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** Projects canonical Consent claims to native FHIR R5. ODRL stays in sourceAttachment. */
|
|
143
|
+
export function consentFlatToFhirR5(claims) {
|
|
144
|
+
const category = conceptFromClaim(claims[ClaimConsent.category], claims[ClaimConsent.categoryDisplay]);
|
|
145
|
+
const scope = conceptFromClaim(claims[ClaimConsent.scope], claims[ClaimConsent.scopeDisplay]);
|
|
146
|
+
const actorReference = claims[ClaimConsent.grantee] || claims[ClaimConsent.actorIdentifier];
|
|
147
|
+
const attachment = attachmentFromClaims(claims);
|
|
148
|
+
return {
|
|
149
|
+
resourceType: ResourceTypesFhirR4.Consent,
|
|
150
|
+
identifier: claims[ClaimConsent.identifier] ? [{ value: claims[ClaimConsent.identifier] }] : undefined,
|
|
151
|
+
status: claims[ClaimConsent.status],
|
|
152
|
+
category: [...(scope || []), ...(category || [])],
|
|
153
|
+
subject: claims[ClaimConsent.subject] ? { reference: claims[ClaimConsent.subject] } : undefined,
|
|
154
|
+
date: claims[ClaimConsent.date],
|
|
155
|
+
period: periodFromClaims(claims, ClaimConsent.periodStart, ClaimConsent.periodEnd),
|
|
156
|
+
grantor: referencesFromClaim(claims[ClaimConsent.grantor]),
|
|
157
|
+
grantee: referencesFromClaim(claims[ClaimConsent.grantee] || claims[ClaimConsent.actorIdentifier]),
|
|
158
|
+
manager: referencesFromClaim(claims[ClaimConsent.manager]),
|
|
159
|
+
controller: referencesFromClaim(claims[ClaimConsent.controller]),
|
|
160
|
+
sourceAttachment: attachment ? [attachment] : undefined,
|
|
161
|
+
sourceReference: referencesFromClaim(claims[ClaimConsent.sourceReference]),
|
|
162
|
+
decision: claims[ClaimConsent.decision],
|
|
163
|
+
provision: [{
|
|
164
|
+
actor: actorReference ? [{ role: conceptWithCodingsFromClaim(claims[ClaimConsent.actorRole]), reference: { reference: actorReference } }] : undefined,
|
|
165
|
+
action: conceptsFromClaim(claims[ClaimConsent.action]),
|
|
166
|
+
securityLabel: codingsFromClaim(claims[ClaimConsent.securityLabel]),
|
|
167
|
+
purpose: codingsFromClaim(claims[ClaimConsent.purpose]),
|
|
168
|
+
documentType: codingsFromClaim(claims[ClaimConsent.documentType]),
|
|
169
|
+
resourceType: codingsFromClaim(claims[ClaimConsent.resourceType]),
|
|
170
|
+
code: conceptsFromClaim(claims[ClaimConsent.provisionCode], claims[ClaimConsent.provisionCodeDisplay]),
|
|
171
|
+
dataPeriod: periodFromClaims(claims, ClaimConsent.dataPeriodStart, ClaimConsent.dataPeriodEnd),
|
|
172
|
+
}],
|
|
77
173
|
};
|
|
78
174
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Reusable, non-production identifiers for Consent R4/R5 projection tests. */
|
|
2
|
+
export declare const CONSENT_PROJECTION_TEST_DATA: Readonly<{
|
|
3
|
+
identifier: "urn:uuid:9f4105a1-fca1-4b92-a38f-cd40f37da36e";
|
|
4
|
+
subject: "Patient/consent-subject-test";
|
|
5
|
+
grantor: "RelatedPerson/consent-grantor-test";
|
|
6
|
+
grantee: "Organization/consent-grantee-test";
|
|
7
|
+
manager: "Organization/consent-manager-test";
|
|
8
|
+
controller: "Organization/consent-enforcer-test";
|
|
9
|
+
createdAt: "2026-09-08";
|
|
10
|
+
consentPeriodStart: "2026-09-08T10:00:00Z";
|
|
11
|
+
consentPeriodEnd: "2026-10-08T10:00:00Z";
|
|
12
|
+
dataPeriodStart: "2025-09-08T00:00:00Z";
|
|
13
|
+
dataPeriodEnd: "2026-09-08T00:00:00Z";
|
|
14
|
+
actorRole: "http://terminology.hl7.org/CodeSystem/v3-RoleCode|PAYOR";
|
|
15
|
+
action: "http://terminology.hl7.org/CodeSystem/consentaction|access";
|
|
16
|
+
purpose: "http://terminology.hl7.org/CodeSystem/v3-ActReason|HPAYMT";
|
|
17
|
+
scope: "http://terminology.hl7.org/CodeSystem/consentscope|patient-privacy";
|
|
18
|
+
category: "http://loinc.org|59284-0";
|
|
19
|
+
resourceType: "http://hl7.org/fhir/fhir-types|Observation";
|
|
20
|
+
provisionCode: "http://loinc.org|85353-1";
|
|
21
|
+
secondProvisionCode: "http://loinc.org|8310-5";
|
|
22
|
+
provisionCodeList: "http://loinc.org|85353-1,http://loinc.org|8310-5";
|
|
23
|
+
securityLabel: "http://terminology.hl7.org/CodeSystem/v3-Confidentiality|R";
|
|
24
|
+
attachmentContentType: "application/odrl+json";
|
|
25
|
+
attachmentData: "eyJAdHlwZSI6IkFncmVlbWVudCJ9";
|
|
26
|
+
odrlOnlyConstraintName: "odrl.profile.max-query-count";
|
|
27
|
+
odrlOnlyConstraintValue: "1";
|
|
28
|
+
}>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright 2026 Conéctate Soluciones y Aplicaciones SL under the Apache License, Version 2.0.
|
|
2
|
+
/** Reusable, non-production identifiers for Consent R4/R5 projection tests. */
|
|
3
|
+
export const CONSENT_PROJECTION_TEST_DATA = Object.freeze({
|
|
4
|
+
identifier: 'urn:uuid:9f4105a1-fca1-4b92-a38f-cd40f37da36e',
|
|
5
|
+
subject: 'Patient/consent-subject-test',
|
|
6
|
+
grantor: 'RelatedPerson/consent-grantor-test',
|
|
7
|
+
grantee: 'Organization/consent-grantee-test',
|
|
8
|
+
manager: 'Organization/consent-manager-test',
|
|
9
|
+
controller: 'Organization/consent-enforcer-test',
|
|
10
|
+
createdAt: '2026-09-08',
|
|
11
|
+
consentPeriodStart: '2026-09-08T10:00:00Z',
|
|
12
|
+
consentPeriodEnd: '2026-10-08T10:00:00Z',
|
|
13
|
+
dataPeriodStart: '2025-09-08T00:00:00Z',
|
|
14
|
+
dataPeriodEnd: '2026-09-08T00:00:00Z',
|
|
15
|
+
actorRole: 'http://terminology.hl7.org/CodeSystem/v3-RoleCode|PAYOR',
|
|
16
|
+
action: 'http://terminology.hl7.org/CodeSystem/consentaction|access',
|
|
17
|
+
purpose: 'http://terminology.hl7.org/CodeSystem/v3-ActReason|HPAYMT',
|
|
18
|
+
scope: 'http://terminology.hl7.org/CodeSystem/consentscope|patient-privacy',
|
|
19
|
+
category: 'http://loinc.org|59284-0',
|
|
20
|
+
resourceType: 'http://hl7.org/fhir/fhir-types|Observation',
|
|
21
|
+
provisionCode: 'http://loinc.org|85353-1',
|
|
22
|
+
secondProvisionCode: 'http://loinc.org|8310-5',
|
|
23
|
+
provisionCodeList: 'http://loinc.org|85353-1,http://loinc.org|8310-5',
|
|
24
|
+
securityLabel: 'http://terminology.hl7.org/CodeSystem/v3-Confidentiality|R',
|
|
25
|
+
attachmentContentType: 'application/odrl+json',
|
|
26
|
+
attachmentData: 'eyJAdHlwZSI6IkFncmVlbWVudCJ9',
|
|
27
|
+
odrlOnlyConstraintName: 'odrl.profile.max-query-count',
|
|
28
|
+
odrlOnlyConstraintValue: '1',
|
|
29
|
+
});
|
package/dist/examples/index.d.ts
CHANGED
package/dist/examples/index.js
CHANGED
|
@@ -13,7 +13,12 @@ export declare enum ClaimConsent {
|
|
|
13
13
|
'date' = "Consent.date",
|
|
14
14
|
'periodStart' = "Consent.period-start",
|
|
15
15
|
'periodEnd' = "Consent.period-end",
|
|
16
|
+
'dataPeriodStart' = "Consent.data-period-start",
|
|
17
|
+
'dataPeriodEnd' = "Consent.data-period-end",
|
|
18
|
+
'grantor' = "Consent.grantor",
|
|
16
19
|
'grantee' = "Consent.grantee",
|
|
20
|
+
'manager' = "Consent.manager",
|
|
21
|
+
'controller' = "Consent.controller",
|
|
17
22
|
'verifiedBy' = "Consent.verified-by",
|
|
18
23
|
'verifiedDate' = "Consent.verified-date",
|
|
19
24
|
'purpose' = "Consent.purpose",
|
|
@@ -25,6 +30,8 @@ export declare enum ClaimConsent {
|
|
|
25
30
|
'policyRule' = "Consent.policy-rule",
|
|
26
31
|
'provisionCode' = "Consent.provision-code",
|
|
27
32
|
'provisionCodeDisplay' = "Consent.provision-code-display",
|
|
33
|
+
'securityLabel' = "Consent.security-label",
|
|
34
|
+
'documentType' = "Consent.document-type",
|
|
28
35
|
'containedReferenceList' = "Consent.contained-reference-list",
|
|
29
36
|
/**
|
|
30
37
|
* @deprecated Use `containedReferenceList`.
|
|
@@ -137,6 +144,14 @@ export interface ConsentRule {
|
|
|
137
144
|
* Derived from the end of the `org.hl7.fhir.api.Consent.period` claim.
|
|
138
145
|
*/
|
|
139
146
|
'Consent.period-end'?: string;
|
|
147
|
+
/** Timeframe of the governed data; distinct from Consent validity. */
|
|
148
|
+
'Consent.data-period-start'?: string;
|
|
149
|
+
'Consent.data-period-end'?: string;
|
|
150
|
+
/** R5 grantor, grantee, workflow manager and enforcement controller references. */
|
|
151
|
+
'Consent.grantor'?: string;
|
|
152
|
+
'Consent.grantee'?: string;
|
|
153
|
+
'Consent.manager'?: string;
|
|
154
|
+
'Consent.controller'?: string;
|
|
140
155
|
/**
|
|
141
156
|
* The party to whom the consent is granted.
|
|
142
157
|
* Use both 'Consent.actor-identifier' and 'Consent.actor-role' instead.
|
|
@@ -163,6 +178,11 @@ export interface ConsentRule {
|
|
|
163
178
|
* Derived from the `org.hl7.fhir.api.Consent.identifier` claim.
|
|
164
179
|
*/
|
|
165
180
|
'Consent.identifier': string;
|
|
181
|
+
/** Governed FHIR resource types and security/content classifications. */
|
|
182
|
+
'Consent.resource-type'?: string;
|
|
183
|
+
'Consent.security-label'?: string;
|
|
184
|
+
'Consent.document-type'?: string;
|
|
185
|
+
'Consent.provision-code'?: string;
|
|
166
186
|
'Consent.contained-reference-list'?: string;
|
|
167
187
|
'Consent.contained-resource-list'?: string;
|
|
168
188
|
'Consent.contained-documents'?: string;
|
|
@@ -15,7 +15,12 @@ export var ClaimConsent;
|
|
|
15
15
|
ClaimConsent["date"] = "Consent.date";
|
|
16
16
|
ClaimConsent["periodStart"] = "Consent.period-start";
|
|
17
17
|
ClaimConsent["periodEnd"] = "Consent.period-end";
|
|
18
|
+
ClaimConsent["dataPeriodStart"] = "Consent.data-period-start";
|
|
19
|
+
ClaimConsent["dataPeriodEnd"] = "Consent.data-period-end";
|
|
20
|
+
ClaimConsent["grantor"] = "Consent.grantor";
|
|
18
21
|
ClaimConsent["grantee"] = "Consent.grantee";
|
|
22
|
+
ClaimConsent["manager"] = "Consent.manager";
|
|
23
|
+
ClaimConsent["controller"] = "Consent.controller";
|
|
19
24
|
ClaimConsent["verifiedBy"] = "Consent.verified-by";
|
|
20
25
|
ClaimConsent["verifiedDate"] = "Consent.verified-date";
|
|
21
26
|
ClaimConsent["purpose"] = "Consent.purpose";
|
|
@@ -27,6 +32,8 @@ export var ClaimConsent;
|
|
|
27
32
|
ClaimConsent["policyRule"] = "Consent.policy-rule";
|
|
28
33
|
ClaimConsent["provisionCode"] = "Consent.provision-code";
|
|
29
34
|
ClaimConsent["provisionCodeDisplay"] = "Consent.provision-code-display";
|
|
35
|
+
ClaimConsent["securityLabel"] = "Consent.security-label";
|
|
36
|
+
ClaimConsent["documentType"] = "Consent.document-type";
|
|
30
37
|
ClaimConsent["containedReferenceList"] = "Consent.contained-reference-list";
|
|
31
38
|
/**
|
|
32
39
|
* @deprecated Use `containedReferenceList`.
|
|
@@ -12,7 +12,11 @@ export type FhirIpsCreatorBundleEntry = Readonly<{
|
|
|
12
12
|
type FhirIpsCreatorBase = Readonly<{
|
|
13
13
|
/** Stable imported or locally generated actor identifier. */
|
|
14
14
|
actorIdentifier: string;
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Historical API name for the stable role/relationship assignment identifier.
|
|
17
|
+
* Provenance uses this assignment as `Composition.attester.party`; it is not
|
|
18
|
+
* automatically the `Composition.author`.
|
|
19
|
+
*/
|
|
16
20
|
authorIdentifier: string;
|
|
17
21
|
}>;
|
|
18
22
|
export type FhirIpsCreatorAuthorInput = (FhirIpsCreatorBase & Readonly<{
|
|
@@ -93,8 +97,11 @@ type ClinicalCreatorChannelAliases = Readonly<{
|
|
|
93
97
|
}>;
|
|
94
98
|
/**
|
|
95
99
|
* Durable clinical-creator identity stored behind the authenticated profile.
|
|
96
|
-
* `actorIdentifier` identifies the person/member.
|
|
97
|
-
*
|
|
100
|
+
* `actorIdentifier` identifies the person/member. The historical wire property
|
|
101
|
+
* `authorIdentifier` identifies the concrete PractitionerRole/RelatedPerson
|
|
102
|
+
* assignment used as `Composition.attester.party`; it does not decide
|
|
103
|
+
* `Composition.author`. `ownerIdentifier` identifies the legal organization or
|
|
104
|
+
* individual organization/subject that owns that assignment.
|
|
98
105
|
* Contacts, DCR clients, keys and operational DIDs are aliases only.
|
|
99
106
|
*/
|
|
100
107
|
export type ClinicalCreatorBinding = ClinicalCreatorChannelAliases & Readonly<{
|
|
@@ -117,7 +124,10 @@ export type ClinicalCreatorBinding = ClinicalCreatorChannelAliases & Readonly<{
|
|
|
117
124
|
export type ClinicalCreatorBindingInput = ClinicalCreatorChannelAliases & Readonly<{
|
|
118
125
|
kind: FhirIpsCreatorKind;
|
|
119
126
|
actorIdentifier: string;
|
|
120
|
-
|
|
127
|
+
/** Preferred high-level name for the PractitionerRole/RelatedPerson UUID. */
|
|
128
|
+
assignmentIdentifier?: string;
|
|
129
|
+
/** @deprecated Historical DCR/profile wire name; use `assignmentIdentifier`. */
|
|
130
|
+
authorIdentifier?: string;
|
|
121
131
|
ownerIdentifier: string;
|
|
122
132
|
role: string;
|
|
123
133
|
}>;
|
|
@@ -129,13 +129,26 @@ export function buildFhirIpsCreatorAuthor(input) {
|
|
|
129
129
|
*/
|
|
130
130
|
export function normalizeClinicalCreatorBinding(input) {
|
|
131
131
|
const actorIdentifier = canonicalUuidUrn(input.actorIdentifier, 'actorIdentifier');
|
|
132
|
-
const
|
|
132
|
+
const preferredAssignment = input.assignmentIdentifier
|
|
133
|
+
? canonicalUuidUrn(input.assignmentIdentifier, 'assignmentIdentifier')
|
|
134
|
+
: undefined;
|
|
135
|
+
const legacyAssignment = input.authorIdentifier
|
|
136
|
+
? canonicalUuidUrn(input.authorIdentifier, 'authorIdentifier')
|
|
137
|
+
: undefined;
|
|
138
|
+
if (preferredAssignment && legacyAssignment && preferredAssignment !== legacyAssignment) {
|
|
139
|
+
throw new TypeError('assignmentIdentifier and deprecated authorIdentifier must identify the same assignment.');
|
|
140
|
+
}
|
|
141
|
+
const authorIdentifier = preferredAssignment || legacyAssignment;
|
|
142
|
+
if (!authorIdentifier) {
|
|
143
|
+
throw new TypeError('assignmentIdentifier is required.');
|
|
144
|
+
}
|
|
133
145
|
const ownerIdentifier = input.kind === FhirIpsCreatorKinds.Professional
|
|
134
146
|
? requireReference(input.ownerIdentifier, 'ownerIdentifier')
|
|
135
147
|
: canonicalUuidUrn(input.ownerIdentifier, 'ownerIdentifier');
|
|
136
148
|
const role = canonicalRoleClaim(input.role);
|
|
149
|
+
const { assignmentIdentifier: _assignmentIdentifier, ...wireInput } = input;
|
|
137
150
|
return {
|
|
138
|
-
...
|
|
151
|
+
...wireInput,
|
|
139
152
|
actorIdentifier,
|
|
140
153
|
authorIdentifier,
|
|
141
154
|
ownerIdentifier,
|