gdc-common-utils-ts 2.8.8 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/constants/healthcare.d.ts +36 -0
- package/dist/constants/healthcare.js +62 -1
- package/dist/constants/identity-identifiers.d.ts +25 -0
- package/dist/constants/identity-identifiers.js +13 -0
- package/dist/constants/sectors.d.ts +3 -0
- package/dist/constants/sectors.js +3 -0
- package/dist/examples/shared.d.ts +8 -0
- package/dist/examples/shared.js +26 -2
- package/dist/utils/did.d.ts +62 -15
- package/dist/utils/did.js +151 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -427,7 +427,9 @@ The canonical API contract should live in JSDoc on exported code. The README act
|
|
|
427
427
|
sections after six extensions, and the complete 82-section/classification
|
|
428
428
|
catalog. The IPS document type lives separately in `HealthcareDocumentTypes`.
|
|
429
429
|
- [`HealthcareConsentPurposes`, `HealthcareConsentActions`, `HealthcareActorRoles`, `HealthcareActorRoleCodes`](src/constants/healthcare.ts)
|
|
430
|
-
- Shared healthcare authorization and role constants
|
|
430
|
+
- Shared healthcare authorization and role constants, including the
|
|
431
|
+
`health-lab`, `animal-lab`, and `onehealth-lab` sector catalogs. See
|
|
432
|
+
[Healthcare Roles and i18n 101](docs/101-HEALTHCARE_ROLES_I18N.md).
|
|
431
433
|
- [`EXAMPLE_PROFESSIONAL_ACCESS_SCENARIOS`](src/examples/professional.ts)
|
|
432
434
|
- Reusable professional role/permission examples tying actor role, consent action, SMART scope, and expected FHIR resource types together.
|
|
433
435
|
- [`DeviceUserClasses`, `DeviceAppTypes`](src/constants/device.ts)
|
|
@@ -454,7 +456,7 @@ The canonical API contract should live in JSDoc on exported code. The README act
|
|
|
454
456
|
- do not teach its `entityId` as if it were the legal organization id
|
|
455
457
|
- Derives the technical ML-DSA/ML-KEM communication identity for a device, portal, or app profile and returns JOSE header templates for `meta.jws.protected` and `meta.jwe.header`.
|
|
456
458
|
- Uses explicit `seedMaterial` for deterministic derivation. Without `seedMaterial`, it defaults to random generation. `mode = deterministic` requires `seedMaterial`.
|
|
457
|
-
- [`buildOrganizationDidWeb(...)`, `buildProfessionalDidWeb(...)`, `buildIndividualDidWeb(...)`](src/utils/did.ts)
|
|
459
|
+
- [`buildOrganizationDidWeb(...)`, `buildProfessionalDidWeb(...)`, `buildIndividualDidWeb(...)`, `buildIndividualMemberDidWebFromPrivateIdentifiers(...)`](src/utils/did.ts)
|
|
458
460
|
- Build canonical data-space `did:web` identifiers for hosted organizations, professionals, and individuals/family actors.
|
|
459
461
|
- [`buildSmartCompositionReadScope(...)`](src/utils/smart-scope.ts)
|
|
460
462
|
- Builds the current CORE GW pinned SMART root scope for `organization/Composition...` token requests.
|
|
@@ -1727,6 +1727,10 @@ export declare const HealthcareActorRoles: Readonly<{
|
|
|
1727
1727
|
readonly MidwiferyProfessional: "ISCO-08|2222";
|
|
1728
1728
|
readonly Paramedic: "ISCO-08|2240";
|
|
1729
1729
|
readonly Veterinarian: "ISCO-08|2250";
|
|
1730
|
+
/** ISCO-08 laboratory occupation for non-medical life-science work. */
|
|
1731
|
+
readonly LifeScienceTechnician: "ISCO-08|3141";
|
|
1732
|
+
/** ISCO-08 laboratory occupation for human medical/pathology work. */
|
|
1733
|
+
readonly MedicalAndPathologyLaboratoryTechnician: "ISCO-08|3212";
|
|
1730
1734
|
readonly VeterinaryTechnicianOrAssistant: "ISCO-08|3240";
|
|
1731
1735
|
/** Administrative health occupation; it does not imply clinical authority. */
|
|
1732
1736
|
readonly MedicalSecretary: "ISCO-08|3344";
|
|
@@ -1750,6 +1754,8 @@ export declare const HealthcareActorRoleCodes: Readonly<{
|
|
|
1750
1754
|
readonly MidwiferyProfessional: "2222";
|
|
1751
1755
|
readonly Paramedic: "2240";
|
|
1752
1756
|
readonly Veterinarian: "2250";
|
|
1757
|
+
readonly LifeScienceTechnician: "3141";
|
|
1758
|
+
readonly MedicalAndPathologyLaboratoryTechnician: "3212";
|
|
1753
1759
|
readonly VeterinaryTechnicianOrAssistant: "3240";
|
|
1754
1760
|
readonly MedicalSecretary: "3344";
|
|
1755
1761
|
}>;
|
|
@@ -1786,13 +1792,16 @@ export declare const HealthcareAllRolesByClaim: Readonly<Record<string, Healthca
|
|
|
1786
1792
|
export declare const HealthcareProfessionalRoleCodesBySector: Readonly<{
|
|
1787
1793
|
readonly "health-care": readonly ("RESPRSN" | "221" | "2211" | "2212" | "2221" | "2222" | "2240" | "3344")[];
|
|
1788
1794
|
readonly "health-research": readonly ("RESPRSN" | "221" | "2211" | "2212" | "3344")[];
|
|
1795
|
+
readonly "health-lab": readonly ("RESPRSN" | "221" | "2212" | "3212" | "3344")[];
|
|
1789
1796
|
readonly "health-tech": readonly "RESPRSN"[];
|
|
1790
1797
|
readonly "health-insurance": readonly "RESPRSN"[];
|
|
1791
1798
|
readonly "animal-care": readonly ("RESPRSN" | "2250" | "3240")[];
|
|
1792
1799
|
readonly "animal-research": readonly ("RESPRSN" | "2250" | "3240")[];
|
|
1800
|
+
readonly "animal-lab": readonly ("RESPRSN" | "2250" | "3141" | "3240")[];
|
|
1793
1801
|
readonly "animal-insurance": readonly "RESPRSN"[];
|
|
1794
1802
|
readonly "animal-tech": readonly "RESPRSN"[];
|
|
1795
1803
|
readonly "onehealth-research": readonly ("RESPRSN" | "221" | "2211" | "2212" | "2250" | "3240" | "3344")[];
|
|
1804
|
+
readonly "onehealth-lab": readonly ("RESPRSN" | "221" | "2212" | "2250" | "3141" | "3212" | "3240" | "3344")[];
|
|
1796
1805
|
readonly "onehealth-tech": readonly "RESPRSN"[];
|
|
1797
1806
|
}>;
|
|
1798
1807
|
export declare const HealthcareProfessionalRolesBySector: Readonly<{
|
|
@@ -1814,6 +1823,15 @@ export declare const HealthcareProfessionalRolesBySector: Readonly<{
|
|
|
1814
1823
|
titleEn: string;
|
|
1815
1824
|
definition?: string;
|
|
1816
1825
|
}>>>;
|
|
1826
|
+
readonly "health-lab": Readonly<Record<string, Readonly<{
|
|
1827
|
+
family: HealthcareRoleFamily;
|
|
1828
|
+
codingSystem: string;
|
|
1829
|
+
code: string;
|
|
1830
|
+
claim: string;
|
|
1831
|
+
i18nKey: `org.ilo.isco-08.${string}` | `org.isco08.${string}` | `org.hl7.terminology.CodeSystem.v3-RoleCode.${string}`;
|
|
1832
|
+
titleEn: string;
|
|
1833
|
+
definition?: string;
|
|
1834
|
+
}>>>;
|
|
1817
1835
|
readonly "health-tech": Readonly<Record<string, Readonly<{
|
|
1818
1836
|
family: HealthcareRoleFamily;
|
|
1819
1837
|
codingSystem: string;
|
|
@@ -1850,6 +1868,15 @@ export declare const HealthcareProfessionalRolesBySector: Readonly<{
|
|
|
1850
1868
|
titleEn: string;
|
|
1851
1869
|
definition?: string;
|
|
1852
1870
|
}>>>;
|
|
1871
|
+
readonly "animal-lab": Readonly<Record<string, Readonly<{
|
|
1872
|
+
family: HealthcareRoleFamily;
|
|
1873
|
+
codingSystem: string;
|
|
1874
|
+
code: string;
|
|
1875
|
+
claim: string;
|
|
1876
|
+
i18nKey: `org.ilo.isco-08.${string}` | `org.isco08.${string}` | `org.hl7.terminology.CodeSystem.v3-RoleCode.${string}`;
|
|
1877
|
+
titleEn: string;
|
|
1878
|
+
definition?: string;
|
|
1879
|
+
}>>>;
|
|
1853
1880
|
readonly "animal-insurance": Readonly<Record<string, Readonly<{
|
|
1854
1881
|
family: HealthcareRoleFamily;
|
|
1855
1882
|
codingSystem: string;
|
|
@@ -1877,6 +1904,15 @@ export declare const HealthcareProfessionalRolesBySector: Readonly<{
|
|
|
1877
1904
|
titleEn: string;
|
|
1878
1905
|
definition?: string;
|
|
1879
1906
|
}>>>;
|
|
1907
|
+
readonly "onehealth-lab": Readonly<Record<string, Readonly<{
|
|
1908
|
+
family: HealthcareRoleFamily;
|
|
1909
|
+
codingSystem: string;
|
|
1910
|
+
code: string;
|
|
1911
|
+
claim: string;
|
|
1912
|
+
i18nKey: `org.ilo.isco-08.${string}` | `org.isco08.${string}` | `org.hl7.terminology.CodeSystem.v3-RoleCode.${string}`;
|
|
1913
|
+
titleEn: string;
|
|
1914
|
+
definition?: string;
|
|
1915
|
+
}>>>;
|
|
1880
1916
|
readonly "onehealth-tech": Readonly<Record<string, Readonly<{
|
|
1881
1917
|
family: HealthcareRoleFamily;
|
|
1882
1918
|
codingSystem: string;
|
|
@@ -359,6 +359,10 @@ export const HealthcareActorRoles = Object.freeze({
|
|
|
359
359
|
MidwiferyProfessional: 'ISCO-08|2222',
|
|
360
360
|
Paramedic: 'ISCO-08|2240',
|
|
361
361
|
Veterinarian: 'ISCO-08|2250',
|
|
362
|
+
/** ISCO-08 laboratory occupation for non-medical life-science work. */
|
|
363
|
+
LifeScienceTechnician: 'ISCO-08|3141',
|
|
364
|
+
/** ISCO-08 laboratory occupation for human medical/pathology work. */
|
|
365
|
+
MedicalAndPathologyLaboratoryTechnician: 'ISCO-08|3212',
|
|
362
366
|
VeterinaryTechnicianOrAssistant: 'ISCO-08|3240',
|
|
363
367
|
/** Administrative health occupation; it does not imply clinical authority. */
|
|
364
368
|
MedicalSecretary: 'ISCO-08|3344',
|
|
@@ -382,6 +386,8 @@ export const HealthcareActorRoleCodes = Object.freeze({
|
|
|
382
386
|
MidwiferyProfessional: '2222',
|
|
383
387
|
Paramedic: '2240',
|
|
384
388
|
Veterinarian: '2250',
|
|
389
|
+
LifeScienceTechnician: '3141',
|
|
390
|
+
MedicalAndPathologyLaboratoryTechnician: '3212',
|
|
385
391
|
VeterinaryTechnicianOrAssistant: '3240',
|
|
386
392
|
MedicalSecretary: '3344',
|
|
387
393
|
});
|
|
@@ -464,6 +470,28 @@ function buildProfessionalIscoRoles() {
|
|
|
464
470
|
titleEn: 'Veterinarian',
|
|
465
471
|
}),
|
|
466
472
|
],
|
|
473
|
+
[
|
|
474
|
+
HealthcareActorRoleCodes.LifeScienceTechnician,
|
|
475
|
+
Object.freeze({
|
|
476
|
+
family: HealthcareRoleFamilies.ProfessionalOccupationIsco08,
|
|
477
|
+
codingSystem: ISCO08_CODING_SYSTEM,
|
|
478
|
+
code: HealthcareActorRoleCodes.LifeScienceTechnician,
|
|
479
|
+
claim: HealthcareActorRoles.LifeScienceTechnician,
|
|
480
|
+
i18nKey: `${ISCO08_I18N_NAMESPACE}.${HealthcareActorRoleCodes.LifeScienceTechnician}`,
|
|
481
|
+
titleEn: 'Life science technician (excluding medical)',
|
|
482
|
+
}),
|
|
483
|
+
],
|
|
484
|
+
[
|
|
485
|
+
HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician,
|
|
486
|
+
Object.freeze({
|
|
487
|
+
family: HealthcareRoleFamilies.ProfessionalOccupationIsco08,
|
|
488
|
+
codingSystem: ISCO08_CODING_SYSTEM,
|
|
489
|
+
code: HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician,
|
|
490
|
+
claim: HealthcareActorRoles.MedicalAndPathologyLaboratoryTechnician,
|
|
491
|
+
i18nKey: `${ISCO08_I18N_NAMESPACE}.${HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician}`,
|
|
492
|
+
titleEn: 'Medical and pathology laboratory technician',
|
|
493
|
+
}),
|
|
494
|
+
],
|
|
467
495
|
[
|
|
468
496
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
469
497
|
Object.freeze({
|
|
@@ -561,6 +589,13 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
561
589
|
HealthcareActorRoleCodes.SpecialistMedicalPractitioner,
|
|
562
590
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
563
591
|
]),
|
|
592
|
+
[DataspaceSectors.HealthLab]: Object.freeze([
|
|
593
|
+
HealthcareActorRoleCodes.Controller,
|
|
594
|
+
HealthcareActorRoleCodes.MedicalDoctors,
|
|
595
|
+
HealthcareActorRoleCodes.SpecialistMedicalPractitioner,
|
|
596
|
+
HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician,
|
|
597
|
+
HealthcareActorRoleCodes.MedicalSecretary,
|
|
598
|
+
]),
|
|
564
599
|
[DataspaceSectors.HealthTech]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
565
600
|
[DataspaceSectors.HealthInsurance]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
566
601
|
[DataspaceSectors.AnimalCare]: Object.freeze([
|
|
@@ -573,6 +608,12 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
573
608
|
HealthcareActorRoleCodes.Veterinarian,
|
|
574
609
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
575
610
|
]),
|
|
611
|
+
[DataspaceSectors.AnimalLab]: Object.freeze([
|
|
612
|
+
HealthcareActorRoleCodes.Controller,
|
|
613
|
+
HealthcareActorRoleCodes.Veterinarian,
|
|
614
|
+
HealthcareActorRoleCodes.LifeScienceTechnician,
|
|
615
|
+
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
616
|
+
]),
|
|
576
617
|
[DataspaceSectors.AnimalInsurance]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
577
618
|
[DataspaceSectors.AnimalTech]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
578
619
|
[DataspaceSectors.OneHealthResearch]: Object.freeze([
|
|
@@ -584,18 +625,31 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
584
625
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
585
626
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
586
627
|
]),
|
|
628
|
+
[DataspaceSectors.OneHealthLab]: Object.freeze([
|
|
629
|
+
HealthcareActorRoleCodes.Controller,
|
|
630
|
+
HealthcareActorRoleCodes.MedicalDoctors,
|
|
631
|
+
HealthcareActorRoleCodes.SpecialistMedicalPractitioner,
|
|
632
|
+
HealthcareActorRoleCodes.Veterinarian,
|
|
633
|
+
HealthcareActorRoleCodes.LifeScienceTechnician,
|
|
634
|
+
HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician,
|
|
635
|
+
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
636
|
+
HealthcareActorRoleCodes.MedicalSecretary,
|
|
637
|
+
]),
|
|
587
638
|
[DataspaceSectors.OneHealthTech]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
588
639
|
});
|
|
589
640
|
export const HealthcareProfessionalRolesBySector = Object.freeze({
|
|
590
641
|
[DataspaceSectors.HealthCare]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthCare]),
|
|
591
642
|
[DataspaceSectors.HealthResearch]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthResearch]),
|
|
643
|
+
[DataspaceSectors.HealthLab]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthLab]),
|
|
592
644
|
[DataspaceSectors.HealthTech]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthTech]),
|
|
593
645
|
[DataspaceSectors.HealthInsurance]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthInsurance]),
|
|
594
646
|
[DataspaceSectors.AnimalCare]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.AnimalCare]),
|
|
595
647
|
[DataspaceSectors.AnimalResearch]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.AnimalResearch]),
|
|
648
|
+
[DataspaceSectors.AnimalLab]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.AnimalLab]),
|
|
596
649
|
[DataspaceSectors.AnimalInsurance]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.AnimalInsurance]),
|
|
597
650
|
[DataspaceSectors.AnimalTech]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.AnimalTech]),
|
|
598
651
|
[DataspaceSectors.OneHealthResearch]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.OneHealthResearch]),
|
|
652
|
+
[DataspaceSectors.OneHealthLab]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.OneHealthLab]),
|
|
599
653
|
[DataspaceSectors.OneHealthTech]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.OneHealthTech]),
|
|
600
654
|
});
|
|
601
655
|
export const HealthcareProfessionalRolesBySectorAndClaim = Object.freeze(Object.fromEntries(Object.values(DataspaceSectors).map((sector) => [
|
|
@@ -614,7 +668,14 @@ export function getHealthcareRolesByFamily(family) {
|
|
|
614
668
|
return HealthcareRolesByFamily[family];
|
|
615
669
|
}
|
|
616
670
|
export function getHealthcareRoleByClaim(claim) {
|
|
617
|
-
|
|
671
|
+
const normalized = String(claim || '').trim();
|
|
672
|
+
const direct = HealthcareAllRolesByClaim[normalized];
|
|
673
|
+
if (direct)
|
|
674
|
+
return direct;
|
|
675
|
+
const fullHl7Prefix = `${HL7_CODING_SYSTEM_V3_ROLE_CODE}|`;
|
|
676
|
+
return normalized.startsWith(fullHl7Prefix)
|
|
677
|
+
? HealthcareAllRolesByClaim[`v3-RoleCode|${normalized.slice(fullHl7Prefix.length)}`]
|
|
678
|
+
: undefined;
|
|
618
679
|
}
|
|
619
680
|
export function getHealthcareProfessionalRolesBySector(sector) {
|
|
620
681
|
return HealthcareProfessionalRolesBySector[sector] || {};
|
|
@@ -32,6 +32,31 @@ export declare const HL7_V2_0203_IDENTIFIER_CODES: Readonly<{
|
|
|
32
32
|
readonly PatientIdentifier: "PI";
|
|
33
33
|
}>;
|
|
34
34
|
export type Hl7V20203IdentifierCode = typeof HL7_V2_0203_IDENTIFIER_CODES[keyof typeof HL7_V2_0203_IDENTIFIER_CODES];
|
|
35
|
+
/**
|
|
36
|
+
* Identifier type tokens accepted by the hosted individual DID builders.
|
|
37
|
+
*
|
|
38
|
+
* `DL`, `PPN`, `NN`, and the other clinical identifier codes retain their
|
|
39
|
+
* HL7 v2-0203 meaning. `UUID`, `EMAIL`, and `PHONE` are GDC index identifier
|
|
40
|
+
* types and must not be described as HL7 codes.
|
|
41
|
+
*/
|
|
42
|
+
export declare const SecureIdTypesIndividual: Readonly<{
|
|
43
|
+
readonly DriverLicense: "DL";
|
|
44
|
+
readonly CitizenshipCard: "CZ";
|
|
45
|
+
readonly NationalNumber: "NN";
|
|
46
|
+
readonly PassportNumber: "PPN";
|
|
47
|
+
readonly JurisdictionalHealthNumber: "JHN";
|
|
48
|
+
readonly WorkPermit: "WP";
|
|
49
|
+
readonly StudyPermit: "SP";
|
|
50
|
+
readonly HealthCard: "HC";
|
|
51
|
+
readonly MemberNumber: "MB";
|
|
52
|
+
readonly SubscriberNumber: "SN";
|
|
53
|
+
readonly DonorRecord: "DR";
|
|
54
|
+
readonly PatientIdentifier: "PI";
|
|
55
|
+
readonly Uuid: "UUID";
|
|
56
|
+
readonly Email: "EMAIL";
|
|
57
|
+
readonly Phone: "PHONE";
|
|
58
|
+
}>;
|
|
59
|
+
export type SecureIdTypeIndividual = typeof SecureIdTypesIndividual[keyof typeof SecureIdTypesIndividual];
|
|
35
60
|
/** Product-neutral document kinds used by subject identifier forms. */
|
|
36
61
|
export declare const INDIVIDUAL_IDENTIFIER_KINDS: Readonly<{
|
|
37
62
|
readonly NationalPerson: "national-person";
|
|
@@ -31,6 +31,19 @@ export const HL7_V2_0203_IDENTIFIER_CODES = Object.freeze({
|
|
|
31
31
|
DonorRecord: 'DR',
|
|
32
32
|
PatientIdentifier: 'PI',
|
|
33
33
|
});
|
|
34
|
+
/**
|
|
35
|
+
* Identifier type tokens accepted by the hosted individual DID builders.
|
|
36
|
+
*
|
|
37
|
+
* `DL`, `PPN`, `NN`, and the other clinical identifier codes retain their
|
|
38
|
+
* HL7 v2-0203 meaning. `UUID`, `EMAIL`, and `PHONE` are GDC index identifier
|
|
39
|
+
* types and must not be described as HL7 codes.
|
|
40
|
+
*/
|
|
41
|
+
export const SecureIdTypesIndividual = Object.freeze({
|
|
42
|
+
Uuid: 'UUID',
|
|
43
|
+
Email: 'EMAIL',
|
|
44
|
+
Phone: 'PHONE',
|
|
45
|
+
...HL7_V2_0203_IDENTIFIER_CODES,
|
|
46
|
+
});
|
|
34
47
|
/** Product-neutral document kinds used by subject identifier forms. */
|
|
35
48
|
export const INDIVIDUAL_IDENTIFIER_KINDS = Object.freeze({
|
|
36
49
|
NationalPerson: 'national-person',
|
|
@@ -11,14 +11,17 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export declare const DataspaceSectors: Readonly<{
|
|
13
13
|
readonly HealthCare: "health-care";
|
|
14
|
+
readonly HealthLab: "health-lab";
|
|
14
15
|
readonly HealthResearch: "health-research";
|
|
15
16
|
readonly HealthTech: "health-tech";
|
|
16
17
|
readonly HealthInsurance: "health-insurance";
|
|
17
18
|
readonly AnimalCare: "animal-care";
|
|
19
|
+
readonly AnimalLab: "animal-lab";
|
|
18
20
|
readonly AnimalResearch: "animal-research";
|
|
19
21
|
readonly AnimalInsurance: "animal-insurance";
|
|
20
22
|
readonly AnimalTech: "animal-tech";
|
|
21
23
|
readonly OneHealthResearch: "onehealth-research";
|
|
24
|
+
readonly OneHealthLab: "onehealth-lab";
|
|
22
25
|
readonly OneHealthTech: "onehealth-tech";
|
|
23
26
|
}>;
|
|
24
27
|
export type DataspaceSector = typeof DataspaceSectors[keyof typeof DataspaceSectors];
|
|
@@ -12,13 +12,16 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export const DataspaceSectors = Object.freeze({
|
|
14
14
|
HealthCare: 'health-care',
|
|
15
|
+
HealthLab: 'health-lab',
|
|
15
16
|
HealthResearch: 'health-research',
|
|
16
17
|
HealthTech: 'health-tech',
|
|
17
18
|
HealthInsurance: 'health-insurance',
|
|
18
19
|
AnimalCare: 'animal-care',
|
|
20
|
+
AnimalLab: 'animal-lab',
|
|
19
21
|
AnimalResearch: 'animal-research',
|
|
20
22
|
AnimalInsurance: 'animal-insurance',
|
|
21
23
|
AnimalTech: 'animal-tech',
|
|
22
24
|
OneHealthResearch: 'onehealth-research',
|
|
25
|
+
OneHealthLab: 'onehealth-lab',
|
|
23
26
|
OneHealthTech: 'onehealth-tech',
|
|
24
27
|
});
|
|
@@ -149,6 +149,8 @@ export declare const EXAMPLE_PROVIDER_DOMAIN: "health-care.provider.example.org"
|
|
|
149
149
|
/** Synthetic public portal domain used to derive organization path DIDs. */
|
|
150
150
|
export declare const EXAMPLE_PUBLIC_PORTAL_DOMAIN: "portal.example.org";
|
|
151
151
|
export declare const EXAMPLE_INDIVIDUAL_MULTIBASE_ID: string;
|
|
152
|
+
/** Synthetic private UUID used to prove one-way hosted individual DID construction. */
|
|
153
|
+
export declare const EXAMPLE_PRIVATE_INDIVIDUAL_UUID: "a87e5b15-aea4-4475-9c7c-40aa88354b6f";
|
|
152
154
|
export declare const EXAMPLE_INDIVIDUAL_MULTIBASE_ID_SECONDARY: string;
|
|
153
155
|
export declare const EXAMPLE_INDIVIDUAL_MULTIBASE_ID_TERTIARY: string;
|
|
154
156
|
export declare const EXAMPLE_INDIVIDUAL_MULTIBASE_ID_CONDITION: string;
|
|
@@ -212,7 +214,13 @@ export declare const EXAMPLE_HEALTHCARE_ACTOR_ROLE_PHYSICIAN: "ISCO-08|2211";
|
|
|
212
214
|
export declare const EXAMPLE_HEALTHCARE_ACTOR_ROLE_GENERALIST_MEDICAL_PRACTITIONER: "ISCO-08|2211";
|
|
213
215
|
export declare const EXAMPLE_HEALTHCARE_ACTOR_ROLE_RECEPTIONIST: "ISCO-08|4226";
|
|
214
216
|
export declare const EXAMPLE_HEALTHCARE_ROLE_PHYSICIAN_TEXT: "physician";
|
|
217
|
+
export declare const EXAMPLE_INDIVIDUAL_CONTROLLER_ROLE_TYPE = "http://terminology.hl7.org/CodeSystem/v3-RoleCode";
|
|
218
|
+
export declare const EXAMPLE_INDIVIDUAL_CONTROLLER_ROLE_VALUE: "RESPRSN";
|
|
215
219
|
export declare const EXAMPLE_RELATED_PERSON_ROLE: "v3-RoleCode|RESPRSN";
|
|
220
|
+
export declare const EXAMPLE_SECURE_ID_VALUE_INDIVIDUAL: string;
|
|
221
|
+
export declare const EXAMPLE_SECURE_ID_VALUE_INDIVIDUAL_CONTROLLER: string;
|
|
222
|
+
export declare const EXAMPLE_HOSTED_INDIVIDUAL_DID: string;
|
|
223
|
+
export declare const EXAMPLE_HOSTED_INDIVIDUAL_CONTROLLER_DID: string;
|
|
216
224
|
export declare const EXAMPLE_RELATED_PERSON_MEMBER_DID: string;
|
|
217
225
|
export declare const EXAMPLE_CLINICAL_SECTION_RESULTS: "LOINC|30954-2";
|
|
218
226
|
export declare const EXAMPLE_CLINICAL_SECTION_PATIENT_SUMMARY: "LOINC|60591-5";
|
package/dist/examples/shared.js
CHANGED
|
@@ -6,12 +6,13 @@ import { FhirCodeSystems } from '../constants/fhir-code-systems.js';
|
|
|
6
6
|
import { Format } from '../constants/Schemas.js';
|
|
7
7
|
import { ResourceTypesFhirR4 } from '../constants/fhir-resource-types.js';
|
|
8
8
|
import { BirthSex, GenderIdentity } from '../constants/identity-gender.js';
|
|
9
|
-
import { IdKind } from '../constants/identity-identifiers.js';
|
|
9
|
+
import { IdKind, SecureIdTypesIndividual } from '../constants/identity-identifiers.js';
|
|
10
|
+
import { HL7_CODING_SYSTEM_V3_ROLE_CODE } from '../constants/hl7-roles.js';
|
|
10
11
|
import { HealthcareActorRoles, HealthcareBasicSections, HealthcareConsentPurposes, HealthcareDocumentTypes, } from '../constants/healthcare.js';
|
|
11
12
|
import { CommunicationCategoryCodes } from '../constants/communication.js';
|
|
12
13
|
import { LOINC_SYSTEM_URL } from '../models/clinical-sections.js';
|
|
13
14
|
import { CommunicationClaim } from '../models/interoperable-claims/communication-claims.js';
|
|
14
|
-
import { buildHostedProviderDidWeb, buildIndividualDidWeb, buildIndividualMemberDidWeb, buildProfessionalDidWeb, buildProviderSectorDidWeb, } from '../utils/did.js';
|
|
15
|
+
import { buildHostedProviderDidWeb, buildIndividualDidWeb, buildIndividualMemberDidWeb, buildSecureIdValueIndividual, buildSecureIdValueMember, buildProfessionalDidWeb, buildProviderSectorDidWeb, } from '../utils/did.js';
|
|
15
16
|
import { encodeHexToMultibase58btc } from '../utils/multibase58.js';
|
|
16
17
|
import { MedicationStatementClaim, MedicationStatementClaimsFhirApiExtended, } from '../models/interoperable-claims/medication-statement-claims.js';
|
|
17
18
|
import { medicationStatementFlatToFhirR4 } from '../utils/clinical-resource-converters.js';
|
|
@@ -167,6 +168,8 @@ export const EXAMPLE_PROVIDER_DOMAIN = 'health-care.provider.example.org';
|
|
|
167
168
|
/** Synthetic public portal domain used to derive organization path DIDs. */
|
|
168
169
|
export const EXAMPLE_PUBLIC_PORTAL_DOMAIN = 'portal.example.org';
|
|
169
170
|
export const EXAMPLE_INDIVIDUAL_MULTIBASE_ID = encodeHexToMultibase58btc('a87e5b15aea444759c7c40aa88354b6f');
|
|
171
|
+
/** Synthetic private UUID used to prove one-way hosted individual DID construction. */
|
|
172
|
+
export const EXAMPLE_PRIVATE_INDIVIDUAL_UUID = 'a87e5b15-aea4-4475-9c7c-40aa88354b6f';
|
|
170
173
|
export const EXAMPLE_INDIVIDUAL_MULTIBASE_ID_SECONDARY = encodeHexToMultibase58btc('b98f6c24bfb545849d8d51bb99465c7e');
|
|
171
174
|
export const EXAMPLE_INDIVIDUAL_MULTIBASE_ID_TERTIARY = encodeHexToMultibase58btc('c39f7d35c0c65695ae9e62cca0576d8f');
|
|
172
175
|
export const EXAMPLE_INDIVIDUAL_MULTIBASE_ID_CONDITION = encodeHexToMultibase58btc('d4a08e46d1d767a6bfaf73ddb1687ea0');
|
|
@@ -251,7 +254,28 @@ export const EXAMPLE_HEALTHCARE_ACTOR_ROLE_PHYSICIAN = HealthcareActorRoles.Phys
|
|
|
251
254
|
export const EXAMPLE_HEALTHCARE_ACTOR_ROLE_GENERALIST_MEDICAL_PRACTITIONER = HealthcareActorRoles.GeneralistMedicalPractitioner;
|
|
252
255
|
export const EXAMPLE_HEALTHCARE_ACTOR_ROLE_RECEPTIONIST = 'ISCO-08|4226';
|
|
253
256
|
export const EXAMPLE_HEALTHCARE_ROLE_PHYSICIAN_TEXT = 'physician';
|
|
257
|
+
export const EXAMPLE_INDIVIDUAL_CONTROLLER_ROLE_TYPE = HL7_CODING_SYSTEM_V3_ROLE_CODE;
|
|
258
|
+
export const EXAMPLE_INDIVIDUAL_CONTROLLER_ROLE_VALUE = EXAMPLE_ORGANIZATION_CONTROLLER_ROLE;
|
|
254
259
|
export const EXAMPLE_RELATED_PERSON_ROLE = 'v3-RoleCode|RESPRSN';
|
|
260
|
+
export const EXAMPLE_SECURE_ID_VALUE_INDIVIDUAL = buildSecureIdValueIndividual({
|
|
261
|
+
secureIdTypeIndividual: SecureIdTypesIndividual.Uuid,
|
|
262
|
+
privateIdValueIndividual: EXAMPLE_PRIVATE_INDIVIDUAL_UUID,
|
|
263
|
+
});
|
|
264
|
+
export const EXAMPLE_SECURE_ID_VALUE_INDIVIDUAL_CONTROLLER = buildSecureIdValueMember({
|
|
265
|
+
secureIdTypeMember: SecureIdTypesIndividual.Email,
|
|
266
|
+
privateIdValueMember: EXAMPLE_EMAIL_CONTROLLER_INDIVIDUAL,
|
|
267
|
+
});
|
|
268
|
+
export const EXAMPLE_HOSTED_INDIVIDUAL_DID = buildIndividualDidWeb({
|
|
269
|
+
providerDidWeb: EXAMPLE_HOSTED_PROVIDER_DID,
|
|
270
|
+
secureIdTypeIndividual: SecureIdTypesIndividual.Uuid,
|
|
271
|
+
secureIdValueIndividual: EXAMPLE_SECURE_ID_VALUE_INDIVIDUAL,
|
|
272
|
+
});
|
|
273
|
+
export const EXAMPLE_HOSTED_INDIVIDUAL_CONTROLLER_DID = buildIndividualMemberDidWeb({
|
|
274
|
+
individualDidWeb: EXAMPLE_HOSTED_INDIVIDUAL_DID,
|
|
275
|
+
memberId: EXAMPLE_SECURE_ID_VALUE_INDIVIDUAL_CONTROLLER,
|
|
276
|
+
roleType: EXAMPLE_INDIVIDUAL_CONTROLLER_ROLE_TYPE,
|
|
277
|
+
roleValue: EXAMPLE_INDIVIDUAL_CONTROLLER_ROLE_VALUE,
|
|
278
|
+
});
|
|
255
279
|
export const EXAMPLE_RELATED_PERSON_MEMBER_DID = buildIndividualMemberDidWeb({
|
|
256
280
|
individualDidWeb: EXAMPLE_SUBJECT_DID,
|
|
257
281
|
role: EXAMPLE_RELATED_PERSON_ROLE,
|
package/dist/utils/did.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ServiceEndpointSelector } from "../models/did";
|
|
2
|
+
import { type SecureIdTypeIndividual } from '../constants/identity-identifiers';
|
|
2
3
|
/**
|
|
3
4
|
* Canonical DID path markers for hosted/provider individual identities.
|
|
4
5
|
*
|
|
@@ -11,6 +12,19 @@ export declare const IndividualDidMarkers: {
|
|
|
11
12
|
readonly Member: "member";
|
|
12
13
|
readonly Role: "role";
|
|
13
14
|
};
|
|
15
|
+
export declare function buildSecureIdValueIndividual(input: {
|
|
16
|
+
secureIdTypeIndividual: SecureIdTypeIndividual | string;
|
|
17
|
+
privateIdValueIndividual: string;
|
|
18
|
+
}): string;
|
|
19
|
+
/**
|
|
20
|
+
* Builds the same SHA3-384 multihash path value for the private identifier of
|
|
21
|
+
* an individual member/controller. The member and represented individual
|
|
22
|
+
* remain distinct inputs even when both refer to the same natural person.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildSecureIdValueMember(input: {
|
|
25
|
+
secureIdTypeMember: SecureIdTypeIndividual | string;
|
|
26
|
+
privateIdValueMember: string;
|
|
27
|
+
}): string;
|
|
14
28
|
/**
|
|
15
29
|
* Removes any coding-system prefix from a role claim when building a member DID.
|
|
16
30
|
*
|
|
@@ -189,38 +203,71 @@ export declare function buildProfessionalDidWeb(input: {
|
|
|
189
203
|
* Builds the canonical individual DID under either a hosted-provider DID root
|
|
190
204
|
* or an external provider-sector DID root.
|
|
191
205
|
*
|
|
192
|
-
* Canonical
|
|
193
|
-
* -
|
|
194
|
-
* `did:web:<host.domain>:<sector>:organization:taxid:<provider-tax-id>:individual:multibase:<individualId>`
|
|
195
|
-
* - external/provider-domain:
|
|
196
|
-
* `did:web:<sector.provider.domain>:individual:multibase:<individualId>`
|
|
206
|
+
* Canonical hosted form:
|
|
207
|
+
* `did:web:<provider-path>:individual:<secureIdTypeIndividual>:<secureIdValueIndividual>`
|
|
197
208
|
*
|
|
198
209
|
* Important semantics:
|
|
199
|
-
* -
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
210
|
+
* - the type remains an explicit upper-case path token such as `UUID`, `DL`,
|
|
211
|
+
* `PPN`, `EMAIL`, or `PHONE`;
|
|
212
|
+
* - the value is always the one-way SHA3-384 multihash built by
|
|
213
|
+
* `buildSecureIdValueIndividual`, never the private identifier in plain text;
|
|
214
|
+
* - role is not part of the individual DID.
|
|
204
215
|
*
|
|
205
216
|
* Backward-compatibility:
|
|
206
217
|
* - `subjectId` is accepted as a legacy alias for `individualId`
|
|
207
218
|
*/
|
|
208
219
|
export declare function buildIndividualDidWeb(input: {
|
|
209
220
|
providerDidWeb: string;
|
|
221
|
+
secureIdTypeIndividual?: SecureIdTypeIndividual | string;
|
|
222
|
+
secureIdValueIndividual?: string;
|
|
223
|
+
/** @deprecated Use `secureIdTypeIndividual` plus `secureIdValueIndividual`. */
|
|
210
224
|
individualId?: string;
|
|
225
|
+
/** @deprecated Use `secureIdTypeIndividual` plus `secureIdValueIndividual`. */
|
|
211
226
|
subjectId?: string;
|
|
212
227
|
}): string;
|
|
213
228
|
/**
|
|
214
229
|
* Builds the canonical member DID under an individual DID.
|
|
215
230
|
*
|
|
216
231
|
* Canonical form:
|
|
217
|
-
* `did:web:...:individual
|
|
232
|
+
* `did:web:...:individual:<type>:<secure-value>:member:<secure-member-value>:<role-value>`
|
|
218
233
|
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* the claims layer.
|
|
234
|
+
* `roleType` is mandatory input so callers cannot accidentally lose the coded
|
|
235
|
+
* role semantics. Only `roleValue` is serialized in the DID because the full
|
|
236
|
+
* FHIR token (`system|value`) remains in the protected claims/licence layer.
|
|
222
237
|
*/
|
|
223
238
|
export declare function buildIndividualMemberDidWeb(input: {
|
|
224
239
|
individualDidWeb: string;
|
|
225
|
-
|
|
240
|
+
memberId?: string;
|
|
241
|
+
roleType?: string;
|
|
242
|
+
roleValue?: string;
|
|
243
|
+
/** @deprecated Use `roleType` plus `roleValue`. */
|
|
244
|
+
role?: string;
|
|
245
|
+
}): string;
|
|
246
|
+
/**
|
|
247
|
+
* Builds a complete hosted individual-member DID directly from the private
|
|
248
|
+
* identifiers already held by an authorized BFF.
|
|
249
|
+
*
|
|
250
|
+
* This is the integration helper for portals: the provider DID returned by GW,
|
|
251
|
+
* the individual's private UUID or other governed identifier, the logged-in
|
|
252
|
+
* member's email/phone/private identifier, and the protected role type/value
|
|
253
|
+
* are sufficient. Neither private identifier is serialized in the result.
|
|
254
|
+
*/
|
|
255
|
+
export declare function buildIndividualMemberDidWebFromPrivateIdentifiers(input: {
|
|
256
|
+
providerDidWeb: string;
|
|
257
|
+
secureIdTypeIndividual: SecureIdTypeIndividual | string;
|
|
258
|
+
privateIdValueIndividual: string;
|
|
259
|
+
secureIdTypeMember: SecureIdTypeIndividual | string;
|
|
260
|
+
privateIdValueMember: string;
|
|
261
|
+
roleType: string;
|
|
262
|
+
roleValue: string;
|
|
226
263
|
}): string;
|
|
264
|
+
/**
|
|
265
|
+
* Parses one hosted individual-member DID without interpreting its role value
|
|
266
|
+
* as authorization. The complete role system and value must still be checked
|
|
267
|
+
* against the protected licence or credential by the consuming gateway.
|
|
268
|
+
*/
|
|
269
|
+
export declare function parseIndividualMemberDidWeb(didWeb: string): {
|
|
270
|
+
individualDidWeb: string;
|
|
271
|
+
memberId: string;
|
|
272
|
+
roleValue: string;
|
|
273
|
+
};
|
package/dist/utils/did.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// crypto-ts/utils/did.ts
|
|
2
2
|
// Copyright 2025 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
3
3
|
import { multibase58MultihashSha3_256 } from './same-as.js';
|
|
4
|
+
import { SecureIdTypesIndividual, } from '../constants/identity-identifiers.js';
|
|
5
|
+
import { normalizePhone } from './consent.js';
|
|
6
|
+
import { encodeMultibaseSha3 } from './multibasehash.js';
|
|
4
7
|
/**
|
|
5
8
|
* Canonical DID path markers for hosted/provider individual identities.
|
|
6
9
|
*
|
|
@@ -13,6 +16,63 @@ export const IndividualDidMarkers = {
|
|
|
13
16
|
Member: 'member',
|
|
14
17
|
Role: 'role',
|
|
15
18
|
};
|
|
19
|
+
const UUID_VALUE_PATTERN = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i;
|
|
20
|
+
const MULTIBASE_SHA3_384_PATTERN = /^z[1-9A-HJ-NP-Za-km-z]+$/;
|
|
21
|
+
/**
|
|
22
|
+
* Builds the privacy-preserving value used by a hosted individual DID.
|
|
23
|
+
*
|
|
24
|
+
* Wire profile: `multibase(base58btc(multihash(SHA3-384, canonical-bytes)))`.
|
|
25
|
+
* The returned value is the bare `z...` path token, not an
|
|
26
|
+
* `urn:multibase:...` value and not a CID.
|
|
27
|
+
*
|
|
28
|
+
* Canonical inputs:
|
|
29
|
+
* - `UUID`: remove hyphens, interpret the 32 hexadecimal digits as the UUID's
|
|
30
|
+
* canonical 16 bytes, then hash those bytes;
|
|
31
|
+
* - `EMAIL`: remove `mailto:`, whitespace and letter-case differences;
|
|
32
|
+
* - `PHONE`: remove `tel:` and normalize the number through the shared phone
|
|
33
|
+
* normalizer (including its E.164 leading `+` when supplied);
|
|
34
|
+
* - HL7 v2-0203 and other governed private identifiers: NFKC-normalized,
|
|
35
|
+
* trimmed, upper-case UTF-8 text. Their type and jurisdiction remain
|
|
36
|
+
* separate governed fields and are never inferred from the value.
|
|
37
|
+
*/
|
|
38
|
+
function buildSecureIdValue(typeInput, valueInput, context) {
|
|
39
|
+
const type = String(typeInput || '').trim().toUpperCase();
|
|
40
|
+
const rawValue = String(valueInput || '').trim();
|
|
41
|
+
if (!type)
|
|
42
|
+
throw new Error(`${context} requires a secure identifier type.`);
|
|
43
|
+
if (!rawValue)
|
|
44
|
+
throw new Error(`${context} requires a private identifier value.`);
|
|
45
|
+
if (type === SecureIdTypesIndividual.Uuid) {
|
|
46
|
+
if (!UUID_VALUE_PATTERN.test(rawValue)) {
|
|
47
|
+
throw new Error(`${context} requires a hexadecimal UUID value.`);
|
|
48
|
+
}
|
|
49
|
+
const hex = rawValue.replace(/-/g, '').toLowerCase();
|
|
50
|
+
const bytes = new Uint8Array(16);
|
|
51
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
52
|
+
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
53
|
+
}
|
|
54
|
+
return encodeMultibaseSha3(bytes, 384);
|
|
55
|
+
}
|
|
56
|
+
const canonicalText = type === SecureIdTypesIndividual.Email
|
|
57
|
+
? rawValue.replace(/^mailto:/i, '').replace(/\s+/g, '').toLowerCase()
|
|
58
|
+
: type === SecureIdTypesIndividual.Phone
|
|
59
|
+
? normalizePhone(rawValue.replace(/^tel:/i, ''))
|
|
60
|
+
: rawValue.normalize('NFKC').toUpperCase();
|
|
61
|
+
if (!canonicalText)
|
|
62
|
+
throw new Error(`${context} requires a valid ${type} value.`);
|
|
63
|
+
return encodeMultibaseSha3(canonicalText, 384);
|
|
64
|
+
}
|
|
65
|
+
export function buildSecureIdValueIndividual(input) {
|
|
66
|
+
return buildSecureIdValue(input.secureIdTypeIndividual, input.privateIdValueIndividual, 'buildSecureIdValueIndividual');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Builds the same SHA3-384 multihash path value for the private identifier of
|
|
70
|
+
* an individual member/controller. The member and represented individual
|
|
71
|
+
* remain distinct inputs even when both refer to the same natural person.
|
|
72
|
+
*/
|
|
73
|
+
export function buildSecureIdValueMember(input) {
|
|
74
|
+
return buildSecureIdValue(input.secureIdTypeMember, input.privateIdValueMember, 'buildSecureIdValueMember');
|
|
75
|
+
}
|
|
16
76
|
/**
|
|
17
77
|
* Removes any coding-system prefix from a role claim when building a member DID.
|
|
18
78
|
*
|
|
@@ -334,27 +394,40 @@ export function buildProfessionalDidWeb(input) {
|
|
|
334
394
|
* Builds the canonical individual DID under either a hosted-provider DID root
|
|
335
395
|
* or an external provider-sector DID root.
|
|
336
396
|
*
|
|
337
|
-
* Canonical
|
|
338
|
-
* -
|
|
339
|
-
* `did:web:<host.domain>:<sector>:organization:taxid:<provider-tax-id>:individual:multibase:<individualId>`
|
|
340
|
-
* - external/provider-domain:
|
|
341
|
-
* `did:web:<sector.provider.domain>:individual:multibase:<individualId>`
|
|
397
|
+
* Canonical hosted form:
|
|
398
|
+
* `did:web:<provider-path>:individual:<secureIdTypeIndividual>:<secureIdValueIndividual>`
|
|
342
399
|
*
|
|
343
400
|
* Important semantics:
|
|
344
|
-
* -
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
401
|
+
* - the type remains an explicit upper-case path token such as `UUID`, `DL`,
|
|
402
|
+
* `PPN`, `EMAIL`, or `PHONE`;
|
|
403
|
+
* - the value is always the one-way SHA3-384 multihash built by
|
|
404
|
+
* `buildSecureIdValueIndividual`, never the private identifier in plain text;
|
|
405
|
+
* - role is not part of the individual DID.
|
|
349
406
|
*
|
|
350
407
|
* Backward-compatibility:
|
|
351
408
|
* - `subjectId` is accepted as a legacy alias for `individualId`
|
|
352
409
|
*/
|
|
353
410
|
export function buildIndividualDidWeb(input) {
|
|
354
411
|
const providerDidWeb = String(input.providerDidWeb || '').trim();
|
|
412
|
+
const secureIdTypeIndividual = String(input.secureIdTypeIndividual || '').trim().toUpperCase();
|
|
413
|
+
const secureIdValueIndividual = String(input.secureIdValueIndividual || '').trim();
|
|
355
414
|
const individualId = String(input.individualId || input.subjectId || '').trim();
|
|
356
415
|
if (!providerDidWeb)
|
|
357
416
|
throw new Error('buildIndividualDidWeb requires providerDidWeb.');
|
|
417
|
+
if (secureIdTypeIndividual || secureIdValueIndividual) {
|
|
418
|
+
if (!secureIdTypeIndividual || !secureIdValueIndividual) {
|
|
419
|
+
throw new Error('buildIndividualDidWeb requires both secure individual id type and value.');
|
|
420
|
+
}
|
|
421
|
+
if (!MULTIBASE_SHA3_384_PATTERN.test(secureIdValueIndividual)) {
|
|
422
|
+
throw new Error('buildIndividualDidWeb requires secureIdValueIndividual as bare multibase base58btc.');
|
|
423
|
+
}
|
|
424
|
+
return [
|
|
425
|
+
providerDidWeb,
|
|
426
|
+
IndividualDidMarkers.Individual,
|
|
427
|
+
secureIdTypeIndividual,
|
|
428
|
+
secureIdValueIndividual,
|
|
429
|
+
].join(':');
|
|
430
|
+
}
|
|
358
431
|
if (!individualId)
|
|
359
432
|
throw new Error('buildIndividualDidWeb requires individualId.');
|
|
360
433
|
return [
|
|
@@ -368,20 +441,83 @@ export function buildIndividualDidWeb(input) {
|
|
|
368
441
|
* Builds the canonical member DID under an individual DID.
|
|
369
442
|
*
|
|
370
443
|
* Canonical form:
|
|
371
|
-
* `did:web:...:individual
|
|
444
|
+
* `did:web:...:individual:<type>:<secure-value>:member:<secure-member-value>:<role-value>`
|
|
372
445
|
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
* the claims layer.
|
|
446
|
+
* `roleType` is mandatory input so callers cannot accidentally lose the coded
|
|
447
|
+
* role semantics. Only `roleValue` is serialized in the DID because the full
|
|
448
|
+
* FHIR token (`system|value`) remains in the protected claims/licence layer.
|
|
376
449
|
*/
|
|
377
450
|
export function buildIndividualMemberDidWeb(input) {
|
|
378
451
|
const individualDidWeb = String(input.individualDidWeb || '').trim();
|
|
379
452
|
if (!individualDidWeb)
|
|
380
453
|
throw new Error('buildIndividualMemberDidWeb requires individualDidWeb.');
|
|
454
|
+
const memberId = String(input.memberId || '').trim();
|
|
455
|
+
const roleType = String(input.roleType || '').trim();
|
|
456
|
+
const roleValue = String(input.roleValue || '').trim();
|
|
457
|
+
if (memberId || roleType || roleValue) {
|
|
458
|
+
if (!MULTIBASE_SHA3_384_PATTERN.test(memberId)) {
|
|
459
|
+
throw new Error('buildIndividualMemberDidWeb requires memberId as bare multibase base58btc.');
|
|
460
|
+
}
|
|
461
|
+
if (!roleType || !roleValue) {
|
|
462
|
+
throw new Error('buildIndividualMemberDidWeb requires both roleType and roleValue.');
|
|
463
|
+
}
|
|
464
|
+
return [individualDidWeb, IndividualDidMarkers.Member, memberId, roleValue].join(':');
|
|
465
|
+
}
|
|
381
466
|
return [
|
|
382
467
|
individualDidWeb,
|
|
383
468
|
IndividualDidMarkers.Member,
|
|
384
469
|
IndividualDidMarkers.Role,
|
|
385
|
-
toDidMemberRoleCode(input.role),
|
|
470
|
+
toDidMemberRoleCode(String(input.role || '')),
|
|
386
471
|
].join(':');
|
|
387
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* Builds a complete hosted individual-member DID directly from the private
|
|
475
|
+
* identifiers already held by an authorized BFF.
|
|
476
|
+
*
|
|
477
|
+
* This is the integration helper for portals: the provider DID returned by GW,
|
|
478
|
+
* the individual's private UUID or other governed identifier, the logged-in
|
|
479
|
+
* member's email/phone/private identifier, and the protected role type/value
|
|
480
|
+
* are sufficient. Neither private identifier is serialized in the result.
|
|
481
|
+
*/
|
|
482
|
+
export function buildIndividualMemberDidWebFromPrivateIdentifiers(input) {
|
|
483
|
+
const individualDidWeb = buildIndividualDidWeb({
|
|
484
|
+
providerDidWeb: input.providerDidWeb,
|
|
485
|
+
secureIdTypeIndividual: input.secureIdTypeIndividual,
|
|
486
|
+
secureIdValueIndividual: buildSecureIdValueIndividual({
|
|
487
|
+
secureIdTypeIndividual: input.secureIdTypeIndividual,
|
|
488
|
+
privateIdValueIndividual: input.privateIdValueIndividual,
|
|
489
|
+
}),
|
|
490
|
+
});
|
|
491
|
+
return buildIndividualMemberDidWeb({
|
|
492
|
+
individualDidWeb,
|
|
493
|
+
memberId: buildSecureIdValueMember({
|
|
494
|
+
secureIdTypeMember: input.secureIdTypeMember,
|
|
495
|
+
privateIdValueMember: input.privateIdValueMember,
|
|
496
|
+
}),
|
|
497
|
+
roleType: input.roleType,
|
|
498
|
+
roleValue: input.roleValue,
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Parses one hosted individual-member DID without interpreting its role value
|
|
503
|
+
* as authorization. The complete role system and value must still be checked
|
|
504
|
+
* against the protected licence or credential by the consuming gateway.
|
|
505
|
+
*/
|
|
506
|
+
export function parseIndividualMemberDidWeb(didWeb) {
|
|
507
|
+
const candidate = String(didWeb || '').trim();
|
|
508
|
+
const marker = `:${IndividualDidMarkers.Member}:`;
|
|
509
|
+
const markerOffset = candidate.lastIndexOf(marker);
|
|
510
|
+
if (!candidate.startsWith('did:web:') || markerOffset < 0) {
|
|
511
|
+
throw new Error('Invalid hosted individual member DID.');
|
|
512
|
+
}
|
|
513
|
+
const individualDidWeb = candidate.slice(0, markerOffset);
|
|
514
|
+
const suffix = candidate.slice(markerOffset + marker.length).split(':');
|
|
515
|
+
const [memberId, roleValue, ...unexpected] = suffix;
|
|
516
|
+
if (!individualDidWeb.includes(`:${IndividualDidMarkers.Individual}:`)
|
|
517
|
+
|| unexpected.length > 0
|
|
518
|
+
|| !MULTIBASE_SHA3_384_PATTERN.test(String(memberId || ''))
|
|
519
|
+
|| !String(roleValue || '').trim()) {
|
|
520
|
+
throw new Error('Invalid hosted individual member DID.');
|
|
521
|
+
}
|
|
522
|
+
return { individualDidWeb, memberId, roleValue };
|
|
523
|
+
}
|