gdc-common-utils-ts 2.3.23 → 2.3.25
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/dist/constants/identity-identifiers.d.ts +14 -0
- package/dist/constants/identity-identifiers.js +14 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +1 -0
- package/dist/models/linked-subject-identifier.d.ts +31 -0
- package/dist/models/linked-subject-identifier.js +1 -0
- package/dist/utils/consent.d.ts +16 -0
- package/dist/utils/consent.js +28 -2
- package/dist/utils/individual-identifier.d.ts +2 -0
- package/dist/utils/individual-identifier.js +17 -0
- package/package.json +1 -1
|
@@ -26,6 +26,8 @@ export declare const HL7_V2_0203_IDENTIFIER_CODES: Readonly<{
|
|
|
26
26
|
readonly WorkPermit: "WP";
|
|
27
27
|
readonly StudyPermit: "SP";
|
|
28
28
|
readonly HealthCard: "HC";
|
|
29
|
+
readonly MemberNumber: "MB";
|
|
30
|
+
readonly SubscriberNumber: "SN";
|
|
29
31
|
readonly DonorRecord: "DR";
|
|
30
32
|
readonly PatientIdentifier: "PI";
|
|
31
33
|
}>;
|
|
@@ -35,6 +37,9 @@ export declare const INDIVIDUAL_IDENTIFIER_KINDS: Readonly<{
|
|
|
35
37
|
readonly NationalPerson: "national-person";
|
|
36
38
|
readonly Passport: "passport";
|
|
37
39
|
readonly HealthNumber: "health-number";
|
|
40
|
+
readonly HealthCard: "health-card";
|
|
41
|
+
readonly InsuranceMember: "insurance-member";
|
|
42
|
+
readonly InsuranceSubscriber: "insurance-subscriber";
|
|
38
43
|
readonly DriverLicense: "driver-license";
|
|
39
44
|
readonly WorkPermit: "work-permit";
|
|
40
45
|
readonly StudyPermit: "study-permit";
|
|
@@ -44,6 +49,9 @@ export declare const INDIVIDUAL_IDENTIFIER_KIND_CODES: Readonly<{
|
|
|
44
49
|
readonly "national-person": "NN";
|
|
45
50
|
readonly passport: "PPN";
|
|
46
51
|
readonly "health-number": "JHN";
|
|
52
|
+
readonly "health-card": "HC";
|
|
53
|
+
readonly "insurance-member": "MB";
|
|
54
|
+
readonly "insurance-subscriber": "SN";
|
|
47
55
|
readonly "driver-license": "DL";
|
|
48
56
|
readonly "work-permit": "WP";
|
|
49
57
|
readonly "study-permit": "SP";
|
|
@@ -69,6 +77,10 @@ export declare enum IdKind {
|
|
|
69
77
|
PersonalStudyPermit = "org.hl7.terminology.CodeSystem.v2-0203.SP",
|
|
70
78
|
/** HL7 local/private health card identifier. */
|
|
71
79
|
LocalHealthCard = "org.hl7.terminology.CodeSystem.v2-0203.HC",
|
|
80
|
+
/** HL7 insured/member number, including private and travel health insurance. */
|
|
81
|
+
InsuranceMemberNumber = "org.hl7.terminology.CodeSystem.v2-0203.MB",
|
|
82
|
+
/** HL7 policy subscriber number when it differs from the beneficiary/member. */
|
|
83
|
+
InsuranceSubscriberNumber = "org.hl7.terminology.CodeSystem.v2-0203.SN",
|
|
72
84
|
/** HL7 donor record identifier. */
|
|
73
85
|
LocalDonorRecord = "org.hl7.terminology.CodeSystem.v2-0203.DR",
|
|
74
86
|
/** HL7 local patient identifier. */
|
|
@@ -83,6 +95,8 @@ export declare const HL7_V2_0203_REVERSE_DNS_TYPES: Readonly<{
|
|
|
83
95
|
readonly WP: IdKind.PersonalWorkPermit;
|
|
84
96
|
readonly SP: IdKind.PersonalStudyPermit;
|
|
85
97
|
readonly HC: IdKind.LocalHealthCard;
|
|
98
|
+
readonly MB: IdKind.InsuranceMemberNumber;
|
|
99
|
+
readonly SN: IdKind.InsuranceSubscriberNumber;
|
|
86
100
|
readonly DR: IdKind.LocalDonorRecord;
|
|
87
101
|
readonly PI: IdKind.LocalPatientIdentifier;
|
|
88
102
|
}>;
|
|
@@ -26,6 +26,8 @@ export const HL7_V2_0203_IDENTIFIER_CODES = Object.freeze({
|
|
|
26
26
|
WorkPermit: 'WP',
|
|
27
27
|
StudyPermit: 'SP',
|
|
28
28
|
HealthCard: 'HC',
|
|
29
|
+
MemberNumber: 'MB',
|
|
30
|
+
SubscriberNumber: 'SN',
|
|
29
31
|
DonorRecord: 'DR',
|
|
30
32
|
PatientIdentifier: 'PI',
|
|
31
33
|
});
|
|
@@ -34,6 +36,9 @@ export const INDIVIDUAL_IDENTIFIER_KINDS = Object.freeze({
|
|
|
34
36
|
NationalPerson: 'national-person',
|
|
35
37
|
Passport: 'passport',
|
|
36
38
|
HealthNumber: 'health-number',
|
|
39
|
+
HealthCard: 'health-card',
|
|
40
|
+
InsuranceMember: 'insurance-member',
|
|
41
|
+
InsuranceSubscriber: 'insurance-subscriber',
|
|
37
42
|
DriverLicense: 'driver-license',
|
|
38
43
|
WorkPermit: 'work-permit',
|
|
39
44
|
StudyPermit: 'study-permit',
|
|
@@ -42,6 +47,9 @@ export const INDIVIDUAL_IDENTIFIER_KIND_CODES = Object.freeze({
|
|
|
42
47
|
[INDIVIDUAL_IDENTIFIER_KINDS.NationalPerson]: HL7_V2_0203_IDENTIFIER_CODES.NationalNumber,
|
|
43
48
|
[INDIVIDUAL_IDENTIFIER_KINDS.Passport]: HL7_V2_0203_IDENTIFIER_CODES.PassportNumber,
|
|
44
49
|
[INDIVIDUAL_IDENTIFIER_KINDS.HealthNumber]: HL7_V2_0203_IDENTIFIER_CODES.JurisdictionalHealthNumber,
|
|
50
|
+
[INDIVIDUAL_IDENTIFIER_KINDS.HealthCard]: HL7_V2_0203_IDENTIFIER_CODES.HealthCard,
|
|
51
|
+
[INDIVIDUAL_IDENTIFIER_KINDS.InsuranceMember]: HL7_V2_0203_IDENTIFIER_CODES.MemberNumber,
|
|
52
|
+
[INDIVIDUAL_IDENTIFIER_KINDS.InsuranceSubscriber]: HL7_V2_0203_IDENTIFIER_CODES.SubscriberNumber,
|
|
45
53
|
[INDIVIDUAL_IDENTIFIER_KINDS.DriverLicense]: HL7_V2_0203_IDENTIFIER_CODES.DriverLicense,
|
|
46
54
|
[INDIVIDUAL_IDENTIFIER_KINDS.WorkPermit]: HL7_V2_0203_IDENTIFIER_CODES.WorkPermit,
|
|
47
55
|
[INDIVIDUAL_IDENTIFIER_KINDS.StudyPermit]: HL7_V2_0203_IDENTIFIER_CODES.StudyPermit,
|
|
@@ -68,6 +76,10 @@ export var IdKind;
|
|
|
68
76
|
IdKind["PersonalStudyPermit"] = "org.hl7.terminology.CodeSystem.v2-0203.SP";
|
|
69
77
|
/** HL7 local/private health card identifier. */
|
|
70
78
|
IdKind["LocalHealthCard"] = "org.hl7.terminology.CodeSystem.v2-0203.HC";
|
|
79
|
+
/** HL7 insured/member number, including private and travel health insurance. */
|
|
80
|
+
IdKind["InsuranceMemberNumber"] = "org.hl7.terminology.CodeSystem.v2-0203.MB";
|
|
81
|
+
/** HL7 policy subscriber number when it differs from the beneficiary/member. */
|
|
82
|
+
IdKind["InsuranceSubscriberNumber"] = "org.hl7.terminology.CodeSystem.v2-0203.SN";
|
|
71
83
|
/** HL7 donor record identifier. */
|
|
72
84
|
IdKind["LocalDonorRecord"] = "org.hl7.terminology.CodeSystem.v2-0203.DR";
|
|
73
85
|
/** HL7 local patient identifier. */
|
|
@@ -82,6 +94,8 @@ export const HL7_V2_0203_REVERSE_DNS_TYPES = Object.freeze({
|
|
|
82
94
|
[HL7_V2_0203_IDENTIFIER_CODES.WorkPermit]: IdKind.PersonalWorkPermit,
|
|
83
95
|
[HL7_V2_0203_IDENTIFIER_CODES.StudyPermit]: IdKind.PersonalStudyPermit,
|
|
84
96
|
[HL7_V2_0203_IDENTIFIER_CODES.HealthCard]: IdKind.LocalHealthCard,
|
|
97
|
+
[HL7_V2_0203_IDENTIFIER_CODES.MemberNumber]: IdKind.InsuranceMemberNumber,
|
|
98
|
+
[HL7_V2_0203_IDENTIFIER_CODES.SubscriberNumber]: IdKind.InsuranceSubscriberNumber,
|
|
85
99
|
[HL7_V2_0203_IDENTIFIER_CODES.DonorRecord]: IdKind.LocalDonorRecord,
|
|
86
100
|
[HL7_V2_0203_IDENTIFIER_CODES.PatientIdentifier]: IdKind.LocalPatientIdentifier,
|
|
87
101
|
});
|
package/dist/models/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export * from './animal-onboarding';
|
|
|
30
30
|
export * from './inter-tenant-access-contract';
|
|
31
31
|
export * from './issue';
|
|
32
32
|
export * from './jsonapi';
|
|
33
|
+
export * from './linked-subject-identifier';
|
|
33
34
|
export * from './jwe';
|
|
34
35
|
export * from './jwk';
|
|
35
36
|
export * from './jws';
|
package/dist/models/index.js
CHANGED
|
@@ -30,6 +30,7 @@ export * from './animal-onboarding.js';
|
|
|
30
30
|
export * from './inter-tenant-access-contract.js';
|
|
31
31
|
export * from './issue.js';
|
|
32
32
|
export * from './jsonapi.js';
|
|
33
|
+
export * from './linked-subject-identifier.js';
|
|
33
34
|
export * from './jwe.js';
|
|
34
35
|
export * from './jwk.js';
|
|
35
36
|
export * from './jws.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Hl7V20203IdentifierCode } from '../constants/identity-identifiers.js';
|
|
2
|
+
/**
|
|
3
|
+
* A confidential external identifier linked to one immutable subject DID.
|
|
4
|
+
*
|
|
5
|
+
* The raw value, assigner and validity period belong in encrypted tenant
|
|
6
|
+
* storage. Only a canonical digest/CID may be used for equality lookup or a
|
|
7
|
+
* future ledger evidence anchor. `MB` identifies an insured member;
|
|
8
|
+
* policy/payor details belong in a FHIR Coverage resource.
|
|
9
|
+
*/
|
|
10
|
+
export type LinkedSubjectIdentifier = Readonly<{
|
|
11
|
+
id: string;
|
|
12
|
+
subjectDid: string;
|
|
13
|
+
type: Hl7V20203IdentifierCode;
|
|
14
|
+
jurisdiction: string;
|
|
15
|
+
value: string;
|
|
16
|
+
assigner?: string;
|
|
17
|
+
periodStart?: string;
|
|
18
|
+
periodEnd?: string;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
}>;
|
|
22
|
+
/** Input accepted by a subject-scoped identifier write boundary. */
|
|
23
|
+
export type LinkedSubjectIdentifierDraft = Readonly<{
|
|
24
|
+
subjectDid: string;
|
|
25
|
+
type: Hl7V20203IdentifierCode;
|
|
26
|
+
jurisdiction: string;
|
|
27
|
+
value: string;
|
|
28
|
+
assigner?: string;
|
|
29
|
+
periodStart?: string;
|
|
30
|
+
periodEnd?: string;
|
|
31
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils/consent.d.ts
CHANGED
|
@@ -53,6 +53,14 @@ export type BuildConsentClaimsSimpleInput = SubjectIdentifierInput & {
|
|
|
53
53
|
actions: string[];
|
|
54
54
|
consentIdentifier?: string;
|
|
55
55
|
consentDate?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Upper bound for the grant, serialized as `Consent.period-end`. Access is
|
|
58
|
+
* inactive at and after this instant.
|
|
59
|
+
*
|
|
60
|
+
* Use an ISO 8601 date or date-time. A date-time with an explicit timezone
|
|
61
|
+
* is recommended when the caller offers access until a specific hour.
|
|
62
|
+
*/
|
|
63
|
+
periodEnd?: string;
|
|
56
64
|
decision?: 'permit' | 'deny';
|
|
57
65
|
/** Stable identifier/thread of the permission request that caused this decision. */
|
|
58
66
|
eventBasedOn?: string;
|
|
@@ -90,6 +98,14 @@ export declare function normalizePhone(value: string): string;
|
|
|
90
98
|
* @param value Raw identifier text.
|
|
91
99
|
*/
|
|
92
100
|
export declare function normalizeIdentifierToken(value: string): string;
|
|
101
|
+
/**
|
|
102
|
+
* Returns whether a Consent period boundary is an ISO 8601 calendar date or
|
|
103
|
+
* an absolute date-time carrying `Z` or an explicit numeric timezone.
|
|
104
|
+
*
|
|
105
|
+
* Date-only values remain supported for compatibility. Applications offering
|
|
106
|
+
* access until a specific hour should always send an absolute date-time.
|
|
107
|
+
*/
|
|
108
|
+
export declare function isValidConsentPeriodBoundary(value: string): boolean;
|
|
93
109
|
/**
|
|
94
110
|
* Parses a single canonical consent actor token into its resolved kind/value.
|
|
95
111
|
*
|
package/dist/utils/consent.js
CHANGED
|
@@ -24,6 +24,27 @@ export function normalizeIdentifierToken(value) {
|
|
|
24
24
|
function withPrefix(prefix, message) {
|
|
25
25
|
return prefix ? `${prefix} ${message}` : message;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Returns whether a Consent period boundary is an ISO 8601 calendar date or
|
|
29
|
+
* an absolute date-time carrying `Z` or an explicit numeric timezone.
|
|
30
|
+
*
|
|
31
|
+
* Date-only values remain supported for compatibility. Applications offering
|
|
32
|
+
* access until a specific hour should always send an absolute date-time.
|
|
33
|
+
*/
|
|
34
|
+
export function isValidConsentPeriodBoundary(value) {
|
|
35
|
+
const normalized = String(value || '').trim();
|
|
36
|
+
if (!/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:\d{2}))?$/.test(normalized)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const [year, month, day] = normalized.slice(0, 10).split('-').map(Number);
|
|
40
|
+
const calendarDate = new Date(Date.UTC(year, month - 1, day));
|
|
41
|
+
if (calendarDate.getUTCFullYear() !== year
|
|
42
|
+
|| calendarDate.getUTCMonth() !== month - 1
|
|
43
|
+
|| calendarDate.getUTCDate() !== day) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return !Number.isNaN(Date.parse(normalized));
|
|
47
|
+
}
|
|
27
48
|
/**
|
|
28
49
|
* Parses a single canonical consent actor token into its resolved kind/value.
|
|
29
50
|
*
|
|
@@ -152,6 +173,10 @@ export function buildConsentClaimsSimple(input, options = {}) {
|
|
|
152
173
|
const actorIdentifier = resolveActorIdentifier(input.actor, { errorPrefix: options.errorPrefix });
|
|
153
174
|
const subjectIdentifier = resolveSubjectIdentifier(input, { errorPrefix: options.errorPrefix });
|
|
154
175
|
const consentDate = String(input.consentDate || '').trim() || new Date().toISOString().slice(0, 10);
|
|
176
|
+
const periodEnd = String(input.periodEnd || '').trim();
|
|
177
|
+
if (periodEnd && !isValidConsentPeriodBoundary(periodEnd)) {
|
|
178
|
+
throw new Error(withPrefix(options.errorPrefix, 'periodEnd must be a valid ISO 8601 date or date-time.'));
|
|
179
|
+
}
|
|
155
180
|
const consentIdentifier = String(input.consentIdentifier || '').trim() || String(options.consentIdentifierFactory?.() || '').trim();
|
|
156
181
|
if (!consentIdentifier) {
|
|
157
182
|
throw new Error(withPrefix(options.errorPrefix, 'consentIdentifier is required when no consentIdentifierFactory is provided.'));
|
|
@@ -165,6 +190,7 @@ export function buildConsentClaimsSimple(input, options = {}) {
|
|
|
165
190
|
[ClaimConsent.subject]: subjectIdentifier,
|
|
166
191
|
[ClaimConsent.identifier]: consentIdentifier,
|
|
167
192
|
[ClaimConsent.date]: consentDate,
|
|
193
|
+
...(periodEnd ? { [ClaimConsent.periodEnd]: periodEnd } : {}),
|
|
168
194
|
[ClaimConsent.purpose]: input.purpose,
|
|
169
195
|
[ClaimConsent.action]: (input.actions || []).join(','),
|
|
170
196
|
[ClaimConsent.actorIdentifier]: actorIdentifier,
|
|
@@ -504,9 +530,9 @@ export function isConsentRuleActive(rule, options = {}) {
|
|
|
504
530
|
: Date.now();
|
|
505
531
|
const periodStart = String(readConsentRuleClaim(rule, ClaimConsent.periodStart) || '').trim();
|
|
506
532
|
const periodEnd = String(readConsentRuleClaim(rule, ClaimConsent.periodEnd) || '').trim();
|
|
507
|
-
if (periodStart &&
|
|
533
|
+
if (periodStart && (Number.isNaN(Date.parse(periodStart)) || Date.parse(periodStart) > now))
|
|
508
534
|
return false;
|
|
509
|
-
if (periodEnd &&
|
|
535
|
+
if (periodEnd && (Number.isNaN(Date.parse(periodEnd)) || Date.parse(periodEnd) <= now))
|
|
510
536
|
return false;
|
|
511
537
|
return true;
|
|
512
538
|
}
|
|
@@ -4,6 +4,8 @@ export type IndividualIdentifierInput = Readonly<{
|
|
|
4
4
|
jurisdiction: string;
|
|
5
5
|
value: string;
|
|
6
6
|
}>;
|
|
7
|
+
/** Validates an optional FHIR Identifier.period expressed as ISO date strings. */
|
|
8
|
+
export declare function assertIndividualIdentifierPeriod(periodStart?: string, periodEnd?: string): void;
|
|
7
9
|
/** Resolves a supported short or reverse-DNS HL7 identifier type to one canonical type. */
|
|
8
10
|
export declare function normalizeIndividualIdentifierType(type: IndividualIdentifierInput['type']): IdKindValue;
|
|
9
11
|
/** Builds the sole canonical input hashed for an external individual identifier alias. */
|
|
@@ -4,6 +4,23 @@ import { encodeMultibaseSha3 } from './multibasehash.js';
|
|
|
4
4
|
import { UrnPrefixes } from '../constants/urn.js';
|
|
5
5
|
const LEGACY_HL7_V2_0203_REVERSE_DNS_PREFIX = 'org.hl7.terminology.codesystem.v2-0203';
|
|
6
6
|
const ISO_3166_JURISDICTION_PATTERN = /^[A-Z]{2}(?:-[A-Z0-9]{1,3})?$/;
|
|
7
|
+
/** Validates an optional FHIR Identifier.period expressed as ISO date strings. */
|
|
8
|
+
export function assertIndividualIdentifierPeriod(periodStart, periodEnd) {
|
|
9
|
+
const start = normalizeOptionalIsoDate(periodStart, 'periodStart');
|
|
10
|
+
const end = normalizeOptionalIsoDate(periodEnd, 'periodEnd');
|
|
11
|
+
if (start && end && start > end) {
|
|
12
|
+
throw new Error('Individual identifier periodStart must not be after periodEnd');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function normalizeOptionalIsoDate(value, field) {
|
|
16
|
+
const normalized = value?.trim();
|
|
17
|
+
if (!normalized)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(normalized) || Number.isNaN(Date.parse(`${normalized}T00:00:00.000Z`))) {
|
|
20
|
+
throw new Error(`Invalid ISO date for ${field}: ${value}`);
|
|
21
|
+
}
|
|
22
|
+
return normalized;
|
|
23
|
+
}
|
|
7
24
|
/** Resolves a supported short or reverse-DNS HL7 identifier type to one canonical type. */
|
|
8
25
|
export function normalizeIndividualIdentifierType(type) {
|
|
9
26
|
const candidate = String(type).trim();
|