gdc-common-utils-ts 2.9.10 → 2.9.12

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 CHANGED
@@ -283,13 +283,23 @@ blockchain asset.
283
283
 
284
284
  ### Convert
285
285
 
286
- The `convert` export exposes resource-specific FHIR R4 conversion helpers such as:
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
 
@@ -677,6 +687,9 @@ when the active runtime variable is really the subject identifier.
677
687
  - [`__tests__/utils-consent-claim-helpers.test.ts`](__tests__/utils-consent-claim-helpers.test.ts)
678
688
 
679
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).
680
693
 
681
694
  ### Public module surfaces
682
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
  }>;
@@ -41,6 +41,7 @@ export const ResourceTypesFhirR4 = Object.freeze({
41
41
  PractitionerRole: 'PractitionerRole',
42
42
  Procedure: 'Procedure',
43
43
  RelatedPerson: 'RelatedPerson',
44
+ ResearchStudy: 'ResearchStudy',
44
45
  ResearchSubject: 'ResearchSubject',
45
46
  Specimen: 'Specimen',
46
47
  });
@@ -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 { codingFromValue, codingToValue, referenceToValue, } from './convert-shared.js';
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 concept = resource.category?.[0];
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.purpose]: codingToValue(provision?.purpose?.[0]),
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.category]: codingToValue(concept?.coding?.[0]),
24
- [ClaimConsent.categoryText]: concept?.text,
25
- [ClaimConsent.categoryDisplay]: concept?.coding?.[0]?.display,
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 coding = codingFromValue(claims[ClaimConsent.category])?.map((item) => ({
35
- ...item,
36
- ...(claims[ClaimConsent.categoryDisplay]
37
- ? { display: claims[ClaimConsent.categoryDisplay] }
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: 'Consent',
80
+ resourceType: ResourceTypesFhirR4.Consent,
46
81
  identifier: claims[ClaimConsent.identifier] ? [{ value: claims[ClaimConsent.identifier] }] : undefined,
47
82
  status: claims[ClaimConsent.status],
48
- scope: scopeCoding ? { coding: scopeCoding } : undefined,
49
- policyRule: claims[ClaimConsent.policyRule] ? { coding: codingFromValue(claims[ClaimConsent.policyRule]) } : undefined,
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
- sourceReference: claims[ClaimConsent.sourceReference]
55
- ? { reference: claims[ClaimConsent.sourceReference] }
56
- : undefined,
57
- category: coding || claims[ClaimConsent.categoryText] ? [{
58
- ...(coding ? { coding } : {}),
59
- ...(claims[ClaimConsent.categoryText] ? { text: claims[ClaimConsent.categoryText] } : {}),
60
- }] : undefined,
61
- provision: claims[ClaimConsent.decision]
62
- || claims[ClaimConsent.periodStart]
63
- || claims[ClaimConsent.periodEnd]
64
- || actionCoding
65
- || purposeCoding
66
- || provisionCodeCoding
67
- ? {
68
- type: claims[ClaimConsent.decision],
69
- period: claims[ClaimConsent.periodStart] || claims[ClaimConsent.periodEnd]
70
- ? { start: claims[ClaimConsent.periodStart], end: claims[ClaimConsent.periodEnd] }
71
- : undefined,
72
- action: actionCoding ? [{ coding: actionCoding }] : undefined,
73
- purpose: purposeCoding,
74
- code: provisionCodeCoding ? [{ coding: provisionCodeCoding }] : undefined,
75
- }
76
- : undefined,
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
+ });
@@ -1,6 +1,7 @@
1
1
  import { OrganizationEmployeeSearchResponseEntryTypes } from '../constants/employee-lifecycle.js';
2
2
  import { ClaimsPersonSchemaorg } from '../constants/schemaorg.js';
3
- import { EXAMPLE_BUNDLE_RESOURCE_TYPE, EXAMPLE_BUNDLE_TYPE_BATCH_RESPONSE, ExampleHttpStatusText, } from './shared.js';
3
+ import { buildProfessionalDidWeb } from '../utils/did.js';
4
+ import { EXAMPLE_API_ORGANIZATION_DID, EXAMPLE_BUNDLE_RESOURCE_TYPE, EXAMPLE_BUNDLE_TYPE_BATCH_RESPONSE, ExampleHttpStatusText, } from './shared.js';
4
5
  export const ExampleEmployeeEmails = Object.freeze({
5
6
  SharedProfessional: 'shared.professional@example.org',
6
7
  });
@@ -14,23 +15,33 @@ export const ExampleEmployeeRoles = Object.freeze({
14
15
  Controller: 'RESPRSN',
15
16
  Doctor: 'ISCO-08|2211',
16
17
  });
18
+ const EXAMPLE_EMPLOYEE_CONTROLLER_DID = buildProfessionalDidWeb({
19
+ organizationDidWeb: EXAMPLE_API_ORGANIZATION_DID,
20
+ email: ExampleEmployeeEmails.SharedProfessional,
21
+ role: ExampleEmployeeRoles.Controller,
22
+ });
23
+ const EXAMPLE_EMPLOYEE_DOCTOR_DID = buildProfessionalDidWeb({
24
+ organizationDidWeb: EXAMPLE_API_ORGANIZATION_DID,
25
+ email: ExampleEmployeeEmails.SharedProfessional,
26
+ role: ExampleEmployeeRoles.Doctor,
27
+ });
17
28
  export const EXAMPLE_EMPLOYEE_CONTROLLER_ACTIVE = Object.freeze({
18
29
  resourceId: 'urn:uuid:11111111-1111-4111-8111-111111111111',
19
- identifier: 'did:web:api.example.org:employee:shared.professional@example.org:RESPRSN',
30
+ identifier: EXAMPLE_EMPLOYEE_CONTROLLER_DID,
20
31
  email: ExampleEmployeeEmails.SharedProfessional,
21
32
  role: ExampleEmployeeRoles.Controller,
22
33
  status: 'active',
23
34
  });
24
35
  export const EXAMPLE_EMPLOYEE_DOCTOR_ACTIVE = Object.freeze({
25
36
  resourceId: 'urn:uuid:22222222-2222-4222-8222-222222222222',
26
- identifier: 'did:web:api.example.org:employee:shared.professional@example.org:ISCO-08|2211',
37
+ identifier: EXAMPLE_EMPLOYEE_DOCTOR_DID,
27
38
  email: ExampleEmployeeEmails.SharedProfessional,
28
39
  role: ExampleEmployeeRoles.Doctor,
29
40
  status: 'active',
30
41
  });
31
42
  export const EXAMPLE_EMPLOYEE_DOCTOR_PURGED_HISTORICAL = Object.freeze({
32
43
  resourceId: 'urn:uuid:33333333-3333-4333-8333-333333333333',
33
- identifier: 'did:web:api.example.org:employee:shared.professional@example.org:ISCO-08|2211:historical:000',
44
+ identifier: `${EXAMPLE_EMPLOYEE_DOCTOR_DID}:historical:000`,
34
45
  email: ExampleEmployeeEmails.SharedProfessional,
35
46
  role: ExampleEmployeeRoles.Doctor,
36
47
  status: 'purged',
@@ -1,4 +1,5 @@
1
1
  export * from './shared';
2
+ export * from './consent-projection';
2
3
  export * from './ica-activation-proof';
3
4
  export * from './ica-verify-response';
4
5
  export * from './dataspace-discovery';
@@ -1,4 +1,5 @@
1
1
  export * from './shared.js';
2
+ export * from './consent-projection.js';
2
3
  export * from './ica-activation-proof.js';
3
4
  export * from './ica-verify-response.js';
4
5
  export * from './dataspace-discovery.js';
@@ -26,7 +26,8 @@ export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SCOPE: "organization/C
26
26
  export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SUBJECT_DID: "did:web:api.acme.org:individual:123";
27
27
  export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SECTION: "LOINC|48765-2";
28
28
  export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_AGREEMENT_PDF_URL: "https://portal.example.org/files/contracts/inter-tenant-contract-acme-lab-001.pdf";
29
- export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_PROFESSIONAL_DID: "did:web:api.lab.org:employee:researcher1@lab.org:ISCO-08|2211";
29
+ export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_PROFESSIONAL_EMAIL: "researcher1@lab.org";
30
+ export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_PROFESSIONAL_DID: string;
30
31
  export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_PROVIDER_AUTHORIZED_SIGNATORY_DID: "did:web:provider-signatory.example.org";
31
32
  export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_AUTHORIZED_SIGNATORY_DID: "did:web:consumer-signatory.example.org";
32
33
  export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SMART_SCOPE: "organization/Composition.rs?subject=did:web:api.acme.org:individual:123&section=LOINC|48765-2";
@@ -59,7 +60,7 @@ export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONTEXT: Readonly<{
59
60
  readonly jurisdiction: "ES";
60
61
  readonly actorRole: "ISCO-08|2211";
61
62
  readonly subjectDid: "did:web:api.acme.org:individual:123";
62
- readonly consumerProfessionalDid: "did:web:api.lab.org:employee:researcher1@lab.org:ISCO-08|2211";
63
+ readonly consumerProfessionalDid: string;
63
64
  readonly requestedScope: "organization/Composition.rs";
64
65
  readonly requestedSection: "LOINC|48765-2";
65
66
  readonly smartScope: "organization/Composition.rs?subject=did:web:api.acme.org:individual:123&section=LOINC|48765-2";
@@ -81,7 +82,7 @@ export declare const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONTEXT_WITH_URNS: Rea
81
82
  readonly jurisdiction: "ES";
82
83
  readonly actorRole: "ISCO-08|2211";
83
84
  readonly subjectDid: "did:web:api.acme.org:individual:123";
84
- readonly consumerProfessionalDid: "did:web:api.lab.org:employee:researcher1@lab.org:ISCO-08|2211";
85
+ readonly consumerProfessionalDid: string;
85
86
  readonly requestedScope: "organization/Composition.rs";
86
87
  readonly requestedSection: "LOINC|48765-2";
87
88
  readonly smartScope: "organization/Composition.rs?subject=did:web:api.acme.org:individual:123&section=LOINC|48765-2";
@@ -1,12 +1,14 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
2
  import { ServiceCapability } from '../constants/service-capabilities.js';
3
- import { HealthcareConsentPurposes } from '../constants/healthcare.js';
3
+ import { HealthcareConsentPurposes, ISCO08_CODING_SYSTEM } from '../constants/healthcare.js';
4
+ import { SecureIdTypesIndividual } from '../constants/identity-identifiers.js';
4
5
  import { DataspaceSectors } from '../constants/sectors.js';
5
6
  import { ClaimConsent } from '../models/consent-rule.js';
6
7
  import { ClaimInterTenantAccessContract } from '../models/inter-tenant-access-contract.js';
7
8
  import { BundleEntryClaimsContext } from '../models/communication-attached-bundle-session.js';
8
9
  import { buildInterTenantAccessContractCredential, buildInterTenantAccessContractResource, getInterTenantAccessContractBlockchainReference, } from '../utils/inter-tenant-access-contract.js';
9
- import { buildMemberAuthorizationUrn, buildOrganizationAuthorizationUrn, } from '../utils/organization-authorization-urn.js';
10
+ import { buildOrganizationAuthorizationUrnCds, buildOrganizationMemberAuthorizationUrnCds, } from '../utils/organization-authorization-urn.js';
11
+ import { buildProfessionalDidWeb, buildSecureIdValueMember } from '../utils/did.js';
10
12
  import { EXAMPLE_API_ORGANIZATION_DID, EXAMPLE_CONTROLLER_DID, EXAMPLE_HEALTHCARE_ACTOR_ROLE_PHYSICIAN, EXAMPLE_JURISDICTION, EXAMPLE_RESEARCH_API_ORGANIZATION_DID, EXAMPLE_RESEARCH_TENANT_IDENTIFIER, EXAMPLE_SECTOR, EXAMPLE_TENANT_IDENTIFIER, } from './shared.js';
11
13
  /**
12
14
  * Shared synthetic inter-tenant contract fixtures reused by GW and SDK tests.
@@ -36,7 +38,12 @@ export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SCOPE = ServiceCapability.Inde
36
38
  export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SUBJECT_DID = 'did:web:api.acme.org:individual:123';
37
39
  export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SECTION = 'LOINC|48765-2';
38
40
  export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_AGREEMENT_PDF_URL = 'https://portal.example.org/files/contracts/inter-tenant-contract-acme-lab-001.pdf';
39
- export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_PROFESSIONAL_DID = `${EXAMPLE_RESEARCH_API_ORGANIZATION_DID}:employee:researcher1@lab.org:${EXAMPLE_HEALTHCARE_ACTOR_ROLE_PHYSICIAN}`;
41
+ export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_PROFESSIONAL_EMAIL = 'researcher1@lab.org';
42
+ export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_PROFESSIONAL_DID = buildProfessionalDidWeb({
43
+ organizationDidWeb: EXAMPLE_RESEARCH_API_ORGANIZATION_DID,
44
+ email: EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_PROFESSIONAL_EMAIL,
45
+ role: EXAMPLE_HEALTHCARE_ACTOR_ROLE_PHYSICIAN,
46
+ });
40
47
  export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_PROVIDER_AUTHORIZED_SIGNATORY_DID = 'did:web:provider-signatory.example.org';
41
48
  export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_AUTHORIZED_SIGNATORY_DID = 'did:web:consumer-signatory.example.org';
42
49
  export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SMART_SCOPE = `${EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SCOPE}?subject=${EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SUBJECT_DID}&section=${EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SECTION}`;
@@ -95,17 +102,24 @@ export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONTEXT = Object.freeze({
95
102
  smartScope: EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_SMART_SCOPE,
96
103
  purpose: EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_PURPOSE,
97
104
  });
98
- export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_PROVIDER_ORGANIZATION_URN = buildOrganizationAuthorizationUrn({
105
+ export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_PROVIDER_ORGANIZATION_URN = buildOrganizationAuthorizationUrnCds({
106
+ jurisdiction: EXAMPLE_JURISDICTION,
99
107
  identifierType: 'TAX',
100
108
  identifierValue: EXAMPLE_TENANT_IDENTIFIER,
101
109
  });
102
- export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_ORGANIZATION_URN = buildOrganizationAuthorizationUrn({
110
+ export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_ORGANIZATION_URN = buildOrganizationAuthorizationUrnCds({
111
+ jurisdiction: EXAMPLE_JURISDICTION,
103
112
  identifierType: 'TAX',
104
113
  identifierValue: EXAMPLE_RESEARCH_TENANT_IDENTIFIER,
105
114
  });
106
- export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_MEMBER_URN = buildMemberAuthorizationUrn({
115
+ export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_MEMBER_URN = buildOrganizationMemberAuthorizationUrnCds({
107
116
  organizationUrn: EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_ORGANIZATION_URN,
108
- memberId: 'researcher-001',
117
+ memberId: buildSecureIdValueMember({
118
+ secureIdTypeMember: SecureIdTypesIndividual.Email,
119
+ privateIdValueMember: EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONSUMER_PROFESSIONAL_EMAIL,
120
+ }),
121
+ roleType: ISCO08_CODING_SYSTEM,
122
+ roleValue: EXAMPLE_HEALTHCARE_ACTOR_ROLE_PHYSICIAN,
109
123
  });
110
124
  export const EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONTEXT_WITH_URNS = Object.freeze({
111
125
  ...EXAMPLE_INTER_TENANT_ACCESS_CONTRACT_CONTEXT,
@@ -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`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "2.9.10",
3
+ "version": "2.9.12",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },