gdc-common-utils-ts 2.8.6 → 2.8.8
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 +9 -0
- package/dist/constants/communication.d.ts +25 -0
- package/dist/constants/communication.js +21 -1
- package/dist/constants/fhir-code-systems.d.ts +1 -0
- package/dist/constants/fhir-code-systems.js +1 -0
- package/dist/examples/license.d.ts +3 -0
- package/dist/examples/license.js +3 -0
- package/dist/models/organization-employee-lifecycle.d.ts +6 -0
- package/dist/utils/fhir-ips-creator-identity.d.ts +13 -5
- package/dist/utils/fhir-ips-creator-identity.js +7 -4
- package/dist/utils/organization-employee-lifecycle.js +24 -5
- package/dist/utils/service-act-reasons.d.ts +1 -1
- package/dist/utils/service-act-reasons.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -417,6 +417,9 @@ The canonical API contract should live in JSDoc on exported code. The README act
|
|
|
417
417
|
- Use instead of inline resource type strings.
|
|
418
418
|
- [`CommunicationCategoryCodes`](src/constants/communication.ts)
|
|
419
419
|
- Canonical `Communication.category` coding descriptors and `<system>|<code>` claims.
|
|
420
|
+
- Exactly the four FHIR R4 categories; use `CommunicationTopicCodes.BreakTheGlass`
|
|
421
|
+
for the HL7 v3 ActReason `BTG` topic and LOINC catalogs for clinical
|
|
422
|
+
section/document topics. See [Communication Layering 101](docs/101-COMMUNICATION_LAYERING.md).
|
|
420
423
|
- [`ObservationCategoryCodes`, `VitalSignsCodes`, `VitalSignsUnits`](src/constants/vital-signs.ts)
|
|
421
424
|
- Canonical Vital Signs category, code, and UCUM unit descriptors for `Observation`.
|
|
422
425
|
- [`HealthcareSummarySections`, `HealthcareCoreSections`, `HealthcareAllSections`](src/constants/healthcare.ts)
|
|
@@ -522,6 +525,12 @@ The canonical API contract should live in JSDoc on exported code. The README act
|
|
|
522
525
|
- The base token examples are read-only; richer scenario fixtures intentionally add `organization/Consent.cruds`.
|
|
523
526
|
- [`src/examples/related-person.ts`](src/examples/related-person.ts)
|
|
524
527
|
- RelatedPerson/family-member examples.
|
|
528
|
+
- [`buildFhirIpsCreatorProvenance(...)`](src/utils/fhir-ips-creator-identity.ts)
|
|
529
|
+
and [`docs/101-DIDCOMM-IDENTITY-BOUNDARY.md`](docs/101-DIDCOMM-IDENTITY-BOUNDARY.md)
|
|
530
|
+
- Keep `Composition.author`, `Composition.attester`, DIDComm sender and
|
|
531
|
+
signing key separate. A member-created fact may use the registered
|
|
532
|
+
RelatedPerson as both author and personal attester; a dictated
|
|
533
|
+
individual-created fact keeps the individual as author.
|
|
525
534
|
- [`src/examples/frontend-session.ts`](src/examples/frontend-session.ts)
|
|
526
535
|
- Frontend profile/session bootstrap examples.
|
|
527
536
|
- [`src/examples/lifecycle.ts`](src/examples/lifecycle.ts)
|
|
@@ -10,6 +10,11 @@ export type CommunicationCategoryDescriptor = Readonly<{
|
|
|
10
10
|
*/
|
|
11
11
|
claim: string;
|
|
12
12
|
}>;
|
|
13
|
+
/**
|
|
14
|
+
* The complete FHIR R4 Communication category value set.
|
|
15
|
+
*
|
|
16
|
+
* @see https://www.hl7.org/fhir/valueset-communication-category.html
|
|
17
|
+
*/
|
|
13
18
|
export declare const CommunicationCategoryCodes: Readonly<{
|
|
14
19
|
readonly Alert: Readonly<{
|
|
15
20
|
system: typeof HL7_COMMUNICATION_CATEGORY_SYSTEM;
|
|
@@ -56,3 +61,23 @@ export declare const CommunicationCategoryCodes: Readonly<{
|
|
|
56
61
|
claim: string;
|
|
57
62
|
}>;
|
|
58
63
|
}>;
|
|
64
|
+
export type CommunicationTopicDescriptor = Readonly<{
|
|
65
|
+
system: string;
|
|
66
|
+
code: string;
|
|
67
|
+
attributeValue: string;
|
|
68
|
+
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Non-LOINC Communication topics explicitly governed by an HL7 code system.
|
|
71
|
+
* Clinical section and document topics continue to use the LOINC catalogs.
|
|
72
|
+
*
|
|
73
|
+
* `BTG` is the HL7 v3 ActReason override purpose used for break-glass alerts.
|
|
74
|
+
* @see http://terminology.hl7.org/CodeSystem/v3-ActReason#v3-ActReason-BTG
|
|
75
|
+
* @see https://profiles.ihe.net/ITI/PCF/Consent-ex-consent-advanced-normal-break-glass-restricted.json.html
|
|
76
|
+
*/
|
|
77
|
+
export declare const CommunicationTopicCodes: Readonly<{
|
|
78
|
+
readonly BreakTheGlass: Readonly<{
|
|
79
|
+
system: string;
|
|
80
|
+
code: string;
|
|
81
|
+
attributeValue: string;
|
|
82
|
+
}>;
|
|
83
|
+
}>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { FhirCodeSystems } from './fhir-code-systems.js';
|
|
2
|
+
export const HL7_COMMUNICATION_CATEGORY_SYSTEM = FhirCodeSystems.CommunicationCategory;
|
|
2
3
|
function defineCommunicationCategory(code) {
|
|
3
4
|
const attributeValue = `${HL7_COMMUNICATION_CATEGORY_SYSTEM}|${code}`;
|
|
4
5
|
return Object.freeze({
|
|
@@ -8,9 +9,28 @@ function defineCommunicationCategory(code) {
|
|
|
8
9
|
claim: attributeValue,
|
|
9
10
|
});
|
|
10
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* The complete FHIR R4 Communication category value set.
|
|
14
|
+
*
|
|
15
|
+
* @see https://www.hl7.org/fhir/valueset-communication-category.html
|
|
16
|
+
*/
|
|
11
17
|
export const CommunicationCategoryCodes = Object.freeze({
|
|
12
18
|
Alert: defineCommunicationCategory('alert'),
|
|
13
19
|
Notification: defineCommunicationCategory('notification'),
|
|
14
20
|
Reminder: defineCommunicationCategory('reminder'),
|
|
15
21
|
Instruction: defineCommunicationCategory('instruction'),
|
|
16
22
|
});
|
|
23
|
+
function defineCommunicationTopic(system, code) {
|
|
24
|
+
return Object.freeze({ system, code, attributeValue: `${system}|${code}` });
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Non-LOINC Communication topics explicitly governed by an HL7 code system.
|
|
28
|
+
* Clinical section and document topics continue to use the LOINC catalogs.
|
|
29
|
+
*
|
|
30
|
+
* `BTG` is the HL7 v3 ActReason override purpose used for break-glass alerts.
|
|
31
|
+
* @see http://terminology.hl7.org/CodeSystem/v3-ActReason#v3-ActReason-BTG
|
|
32
|
+
* @see https://profiles.ihe.net/ITI/PCF/Consent-ex-consent-advanced-normal-break-glass-restricted.json.html
|
|
33
|
+
*/
|
|
34
|
+
export const CommunicationTopicCodes = Object.freeze({
|
|
35
|
+
BreakTheGlass: defineCommunicationTopic(FhirCodeSystems.ActReason, 'BTG'),
|
|
36
|
+
});
|
|
@@ -9,6 +9,7 @@ export declare const FhirCodeSystems: Readonly<{
|
|
|
9
9
|
readonly SnomedCt: "http://snomed.info/sct";
|
|
10
10
|
readonly Ucum: "http://unitsofmeasure.org";
|
|
11
11
|
readonly CommunicationCategory: "http://terminology.hl7.org/CodeSystem/communication-category";
|
|
12
|
+
readonly ActReason: "http://terminology.hl7.org/CodeSystem/v3-ActReason";
|
|
12
13
|
readonly ObservationCategory: "http://terminology.hl7.org/CodeSystem/observation-category";
|
|
13
14
|
readonly EventTiming: "http://hl7.org/fhir/event-timing";
|
|
14
15
|
}>;
|
|
@@ -9,6 +9,7 @@ export const FhirCodeSystems = Object.freeze({
|
|
|
9
9
|
SnomedCt: 'http://snomed.info/sct',
|
|
10
10
|
Ucum: 'http://unitsofmeasure.org',
|
|
11
11
|
CommunicationCategory: 'http://terminology.hl7.org/CodeSystem/communication-category',
|
|
12
|
+
ActReason: 'http://terminology.hl7.org/CodeSystem/v3-ActReason',
|
|
12
13
|
ObservationCategory: 'http://terminology.hl7.org/CodeSystem/observation-category',
|
|
13
14
|
EventTiming: 'http://hl7.org/fhir/event-timing',
|
|
14
15
|
});
|
|
@@ -237,6 +237,9 @@ export declare const EXAMPLE_EMPLOYEE_LIFECYCLE_RECORD: Readonly<{
|
|
|
237
237
|
clientId: "client-employee-primary" | "client-employee-secondary";
|
|
238
238
|
name: "installation-employee-primary" | "installation-employee-secondary" | "Shared browser profile";
|
|
239
239
|
status: "active" | "revoked";
|
|
240
|
+
ownerContact: string;
|
|
241
|
+
ownerContactKind: "email";
|
|
242
|
+
activatedAt: 1786570800 | 1786570900;
|
|
240
243
|
}>[];
|
|
241
244
|
}>;
|
|
242
245
|
}>;
|
package/dist/examples/license.js
CHANGED
|
@@ -171,6 +171,9 @@ export const EXAMPLE_EMPLOYEE_LIFECYCLE_RECORD = Object.freeze({
|
|
|
171
171
|
|| binding.deviceInfo.clientInstanceId
|
|
172
172
|
|| binding.clientId,
|
|
173
173
|
status: binding.status,
|
|
174
|
+
ownerContact: EXAMPLE_EMPLOYEE_CONTROLLER_ACTIVE.email,
|
|
175
|
+
ownerContactKind: 'email',
|
|
176
|
+
activatedAt: binding.activatedAt,
|
|
174
177
|
}))),
|
|
175
178
|
}),
|
|
176
179
|
});
|
|
@@ -5,6 +5,12 @@ export type OrganizationEmployeeDeviceRecord = Readonly<{
|
|
|
5
5
|
clientId: string;
|
|
6
6
|
name: string;
|
|
7
7
|
status: DeviceBindingStatus;
|
|
8
|
+
/** Verified licensed contact that owns this installation. */
|
|
9
|
+
ownerContact?: string;
|
|
10
|
+
/** Contact channel represented by `ownerContact`. */
|
|
11
|
+
ownerContactKind?: 'email' | 'phone';
|
|
12
|
+
/** DCR activation time as Unix epoch seconds. */
|
|
13
|
+
activatedAt?: number;
|
|
8
14
|
}>;
|
|
9
15
|
/** License state attached to one employee directory entry. */
|
|
10
16
|
export type OrganizationEmployeeLicenseRecord = Readonly<{
|
|
@@ -32,7 +32,12 @@ export type FhirIpsCreatorAuthor = Readonly<{
|
|
|
32
32
|
entries: readonly FhirIpsCreatorBundleEntry[];
|
|
33
33
|
}>;
|
|
34
34
|
export type FhirIpsCreatorProvenanceInput = FhirIpsCreatorAuthorInput & Readonly<{
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Explicit source originator. Defaults to the owning organization or
|
|
37
|
+
* individual. When the authenticated member/professional actually authored
|
|
38
|
+
* the content, pass its registered RelatedPerson/PractitionerRole assignment
|
|
39
|
+
* reference; that same assignment may also remain the attester.
|
|
40
|
+
*/
|
|
36
41
|
compositionAuthorReference?: string;
|
|
37
42
|
/** Defaults to professional for employees and personal for individual members. */
|
|
38
43
|
attesterMode?: CompositionAttesterMode;
|
|
@@ -53,10 +58,13 @@ export type FhirIpsCreatorProvenance = Readonly<{
|
|
|
53
58
|
/**
|
|
54
59
|
* Builds source authorship separately from the person or role that attests it.
|
|
55
60
|
*
|
|
56
|
-
* Defaults model
|
|
57
|
-
*
|
|
58
|
-
* subject-authored record.
|
|
59
|
-
*
|
|
61
|
+
* Defaults model delegated flows: an organization's professional attests an
|
|
62
|
+
* organization-authored record, while a member/caregiver attests a
|
|
63
|
+
* subject-authored record. For content actually created by that authenticated
|
|
64
|
+
* member or professional, set `compositionAuthorReference` to its registered
|
|
65
|
+
* RelatedPerson or PractitionerRole assignment. Author and attester may then
|
|
66
|
+
* intentionally reference the same assignment; neither is the transport
|
|
67
|
+
* sender or signing key.
|
|
60
68
|
*/
|
|
61
69
|
export declare function buildFhirIpsCreatorProvenance(input: FhirIpsCreatorProvenanceInput): FhirIpsCreatorProvenance;
|
|
62
70
|
/**
|
|
@@ -11,10 +11,13 @@ export const FhirIpsCreatorKinds = Object.freeze({
|
|
|
11
11
|
/**
|
|
12
12
|
* Builds source authorship separately from the person or role that attests it.
|
|
13
13
|
*
|
|
14
|
-
* Defaults model
|
|
15
|
-
*
|
|
16
|
-
* subject-authored record.
|
|
17
|
-
*
|
|
14
|
+
* Defaults model delegated flows: an organization's professional attests an
|
|
15
|
+
* organization-authored record, while a member/caregiver attests a
|
|
16
|
+
* subject-authored record. For content actually created by that authenticated
|
|
17
|
+
* member or professional, set `compositionAuthorReference` to its registered
|
|
18
|
+
* RelatedPerson or PractitionerRole assignment. Author and attester may then
|
|
19
|
+
* intentionally reference the same assignment; neither is the transport
|
|
20
|
+
* sender or signing key.
|
|
18
21
|
*/
|
|
19
22
|
export function buildFhirIpsCreatorProvenance(input) {
|
|
20
23
|
if (input.kind === FhirIpsCreatorKinds.IndividualSubject) {
|
|
@@ -74,21 +74,33 @@ export function projectOrganizationEmployeeLifecycle(input) {
|
|
|
74
74
|
const claims = record(employee.claims) || record(record(employee.meta)?.claims) || {};
|
|
75
75
|
const resourceId = text(employee.id);
|
|
76
76
|
const employeeDid = text(claims[ClaimsPersonSchemaorg.identifier]);
|
|
77
|
+
const employeeEmail = text(claims[ClaimsPersonSchemaorg.email]);
|
|
78
|
+
const employeeRole = text(claims[ClaimsPersonSchemaorg.hasOccupationalRoleValue]);
|
|
77
79
|
const license = licenses.find((candidate) => {
|
|
78
80
|
const meta = record(candidate.meta) || {};
|
|
79
81
|
const subjectId = text(meta.subjectId);
|
|
80
|
-
|
|
82
|
+
if (subjectId === resourceId || subjectId === employeeDid)
|
|
83
|
+
return true;
|
|
84
|
+
const licenseClaims = record(meta.claims) || record(candidate.claims) || {};
|
|
85
|
+
return Boolean(employeeEmail && employeeRole
|
|
86
|
+
&& text(licenseClaims[ClaimsPersonSchemaorg.email]).toLowerCase() === employeeEmail.toLowerCase()
|
|
87
|
+
&& text(licenseClaims[ClaimsPersonSchemaorg.hasOccupationalRoleValue]) === employeeRole);
|
|
81
88
|
});
|
|
82
89
|
const status = normalizeEmployeeDirectoryStatus(text(employee.status) || text(record(employee.meta)?.status));
|
|
83
90
|
if (!license)
|
|
84
91
|
return {
|
|
85
92
|
resourceId,
|
|
86
|
-
email:
|
|
87
|
-
roleCode:
|
|
93
|
+
email: employeeEmail,
|
|
94
|
+
roleCode: employeeRole,
|
|
88
95
|
employeeDid,
|
|
89
96
|
status,
|
|
90
97
|
};
|
|
91
98
|
const meta = record(license.meta) || {};
|
|
99
|
+
const licenseClaims = record(meta.claims) || record(license.claims) || {};
|
|
100
|
+
const licensedEmail = text(licenseClaims[ClaimsPersonSchemaorg.email]) || employeeEmail;
|
|
101
|
+
const licensedPhone = text(licenseClaims[ClaimsPersonSchemaorg.telephone]);
|
|
102
|
+
const ownerContact = licensedEmail || licensedPhone;
|
|
103
|
+
const ownerContactKind = licensedEmail ? 'email' : licensedPhone ? 'phone' : undefined;
|
|
92
104
|
const bindings = Array.isArray(meta.deviceBindings) ? meta.deviceBindings.map(record).filter(isRecord) : [];
|
|
93
105
|
const devices = bindings.map((binding) => {
|
|
94
106
|
const info = record(binding.deviceInfo) || {};
|
|
@@ -99,12 +111,15 @@ export function projectOrganizationEmployeeLifecycle(input) {
|
|
|
99
111
|
status: binding.status === DeviceBindingStatuses.Revoked
|
|
100
112
|
? DeviceBindingStatuses.Revoked
|
|
101
113
|
: DeviceBindingStatuses.Active,
|
|
114
|
+
...(ownerContact ? { ownerContact } : {}),
|
|
115
|
+
...(ownerContactKind ? { ownerContactKind } : {}),
|
|
116
|
+
...(positiveNumber(binding.activatedAt) ? { activatedAt: positiveNumber(binding.activatedAt) } : {}),
|
|
102
117
|
};
|
|
103
118
|
}).filter((device) => Boolean(device.clientId));
|
|
104
119
|
return {
|
|
105
120
|
resourceId,
|
|
106
|
-
email:
|
|
107
|
-
roleCode:
|
|
121
|
+
email: employeeEmail,
|
|
122
|
+
roleCode: employeeRole,
|
|
108
123
|
employeeDid,
|
|
109
124
|
status,
|
|
110
125
|
license: {
|
|
@@ -156,3 +171,7 @@ function positiveInteger(value) {
|
|
|
156
171
|
const parsed = Number(value);
|
|
157
172
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
158
173
|
}
|
|
174
|
+
function positiveNumber(value) {
|
|
175
|
+
const parsed = Number(value);
|
|
176
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
177
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const HL7_ACT_REASON_CODE_SYSTEM
|
|
1
|
+
export declare const HL7_ACT_REASON_CODE_SYSTEM: "http://terminology.hl7.org/CodeSystem/v3-ActReason";
|
|
2
2
|
export type ServiceActReasonCoding = Readonly<{
|
|
3
3
|
system: string;
|
|
4
4
|
code: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
-
|
|
2
|
+
import { FhirCodeSystems } from '../constants/fhir-code-systems.js';
|
|
3
|
+
export const HL7_ACT_REASON_CODE_SYSTEM = FhirCodeSystems.ActReason;
|
|
3
4
|
function normalizeText(value) {
|
|
4
5
|
return typeof value === 'string' ? value.trim() : '';
|
|
5
6
|
}
|