gdc-common-utils-ts 2.8.8 → 2.9.1
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 +7 -5
- package/dist/constants/healthcare.d.ts +49 -10
- package/dist/constants/healthcare.js +89 -6
- 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/dist/utils/fhir-ips-creator-identity.d.ts +10 -11
- package/dist/utils/fhir-ips-creator-identity.js +16 -8
- 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.
|
|
@@ -528,9 +530,9 @@ The canonical API contract should live in JSDoc on exported code. The README act
|
|
|
528
530
|
- [`buildFhirIpsCreatorProvenance(...)`](src/utils/fhir-ips-creator-identity.ts)
|
|
529
531
|
and [`docs/101-DIDCOMM-IDENTITY-BOUNDARY.md`](docs/101-DIDCOMM-IDENTITY-BOUNDARY.md)
|
|
530
532
|
- Keep `Composition.author`, `Composition.attester`, DIDComm sender and
|
|
531
|
-
signing key separate.
|
|
532
|
-
|
|
533
|
-
|
|
533
|
+
signing key separate. The organization, EHR/portal or individual remains
|
|
534
|
+
the document author; a registered PractitionerRole or RelatedPerson is the
|
|
535
|
+
attester, including for content entered by that authenticated person.
|
|
534
536
|
- [`src/examples/frontend-session.ts`](src/examples/frontend-session.ts)
|
|
535
537
|
- Frontend profile/session bootstrap examples.
|
|
536
538
|
- [`src/examples/lifecycle.ts`](src/examples/lifecycle.ts)
|
|
@@ -1727,7 +1727,13 @@ 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";
|
|
1735
|
+
/** General receptionist occupation usable across human, animal and one-health organizations. */
|
|
1736
|
+
readonly Receptionist: "ISCO-08|4226";
|
|
1731
1737
|
/** Administrative health occupation; it does not imply clinical authority. */
|
|
1732
1738
|
readonly MedicalSecretary: "ISCO-08|3344";
|
|
1733
1739
|
}>;
|
|
@@ -1750,7 +1756,10 @@ export declare const HealthcareActorRoleCodes: Readonly<{
|
|
|
1750
1756
|
readonly MidwiferyProfessional: "2222";
|
|
1751
1757
|
readonly Paramedic: "2240";
|
|
1752
1758
|
readonly Veterinarian: "2250";
|
|
1759
|
+
readonly LifeScienceTechnician: "3141";
|
|
1760
|
+
readonly MedicalAndPathologyLaboratoryTechnician: "3212";
|
|
1753
1761
|
readonly VeterinaryTechnicianOrAssistant: "3240";
|
|
1762
|
+
readonly Receptionist: "4226";
|
|
1754
1763
|
readonly MedicalSecretary: "3344";
|
|
1755
1764
|
}>;
|
|
1756
1765
|
export declare const HealthcareRolesByFamily: Readonly<{
|
|
@@ -1784,16 +1793,19 @@ export declare const HealthcareRolesByFamily: Readonly<{
|
|
|
1784
1793
|
}>;
|
|
1785
1794
|
export declare const HealthcareAllRolesByClaim: Readonly<Record<string, HealthcareActorRoleDescriptor>>;
|
|
1786
1795
|
export declare const HealthcareProfessionalRoleCodesBySector: Readonly<{
|
|
1787
|
-
readonly "health-care": readonly ("RESPRSN" | "221" | "2211" | "2212" | "2221" | "2222" | "2240" | "3344")[];
|
|
1788
|
-
readonly "health-research": readonly ("RESPRSN" | "221" | "2211" | "2212" | "3344")[];
|
|
1789
|
-
readonly "health-
|
|
1790
|
-
readonly "health-
|
|
1791
|
-
readonly "
|
|
1792
|
-
readonly "animal-
|
|
1793
|
-
readonly "animal-
|
|
1794
|
-
readonly "animal-
|
|
1795
|
-
readonly "
|
|
1796
|
-
readonly "
|
|
1796
|
+
readonly "health-care": readonly ("RESPRSN" | "221" | "2211" | "2212" | "2221" | "2222" | "2240" | "4226" | "3344")[];
|
|
1797
|
+
readonly "health-research": readonly ("RESPRSN" | "221" | "2211" | "2212" | "4226" | "3344")[];
|
|
1798
|
+
readonly "health-lab": readonly ("RESPRSN" | "221" | "2212" | "3212" | "4226" | "3344")[];
|
|
1799
|
+
readonly "health-tech": readonly ("RESPRSN" | "4226")[];
|
|
1800
|
+
readonly "health-insurance": readonly ("RESPRSN" | "4226")[];
|
|
1801
|
+
readonly "animal-care": readonly ("RESPRSN" | "2250" | "3240" | "4226")[];
|
|
1802
|
+
readonly "animal-research": readonly ("RESPRSN" | "2250" | "3240" | "4226")[];
|
|
1803
|
+
readonly "animal-lab": readonly ("RESPRSN" | "2250" | "3141" | "3240" | "4226")[];
|
|
1804
|
+
readonly "animal-insurance": readonly ("RESPRSN" | "4226")[];
|
|
1805
|
+
readonly "animal-tech": readonly ("RESPRSN" | "4226")[];
|
|
1806
|
+
readonly "onehealth-research": readonly ("RESPRSN" | "221" | "2211" | "2212" | "2250" | "3240" | "4226" | "3344")[];
|
|
1807
|
+
readonly "onehealth-lab": readonly ("RESPRSN" | "221" | "2212" | "2250" | "3141" | "3212" | "3240" | "4226" | "3344")[];
|
|
1808
|
+
readonly "onehealth-tech": readonly ("RESPRSN" | "4226")[];
|
|
1797
1809
|
}>;
|
|
1798
1810
|
export declare const HealthcareProfessionalRolesBySector: Readonly<{
|
|
1799
1811
|
readonly "health-care": Readonly<Record<string, Readonly<{
|
|
@@ -1814,6 +1826,15 @@ export declare const HealthcareProfessionalRolesBySector: Readonly<{
|
|
|
1814
1826
|
titleEn: string;
|
|
1815
1827
|
definition?: string;
|
|
1816
1828
|
}>>>;
|
|
1829
|
+
readonly "health-lab": Readonly<Record<string, Readonly<{
|
|
1830
|
+
family: HealthcareRoleFamily;
|
|
1831
|
+
codingSystem: string;
|
|
1832
|
+
code: string;
|
|
1833
|
+
claim: string;
|
|
1834
|
+
i18nKey: `org.ilo.isco-08.${string}` | `org.isco08.${string}` | `org.hl7.terminology.CodeSystem.v3-RoleCode.${string}`;
|
|
1835
|
+
titleEn: string;
|
|
1836
|
+
definition?: string;
|
|
1837
|
+
}>>>;
|
|
1817
1838
|
readonly "health-tech": Readonly<Record<string, Readonly<{
|
|
1818
1839
|
family: HealthcareRoleFamily;
|
|
1819
1840
|
codingSystem: string;
|
|
@@ -1850,6 +1871,15 @@ export declare const HealthcareProfessionalRolesBySector: Readonly<{
|
|
|
1850
1871
|
titleEn: string;
|
|
1851
1872
|
definition?: string;
|
|
1852
1873
|
}>>>;
|
|
1874
|
+
readonly "animal-lab": Readonly<Record<string, Readonly<{
|
|
1875
|
+
family: HealthcareRoleFamily;
|
|
1876
|
+
codingSystem: string;
|
|
1877
|
+
code: string;
|
|
1878
|
+
claim: string;
|
|
1879
|
+
i18nKey: `org.ilo.isco-08.${string}` | `org.isco08.${string}` | `org.hl7.terminology.CodeSystem.v3-RoleCode.${string}`;
|
|
1880
|
+
titleEn: string;
|
|
1881
|
+
definition?: string;
|
|
1882
|
+
}>>>;
|
|
1853
1883
|
readonly "animal-insurance": Readonly<Record<string, Readonly<{
|
|
1854
1884
|
family: HealthcareRoleFamily;
|
|
1855
1885
|
codingSystem: string;
|
|
@@ -1877,6 +1907,15 @@ export declare const HealthcareProfessionalRolesBySector: Readonly<{
|
|
|
1877
1907
|
titleEn: string;
|
|
1878
1908
|
definition?: string;
|
|
1879
1909
|
}>>>;
|
|
1910
|
+
readonly "onehealth-lab": Readonly<Record<string, Readonly<{
|
|
1911
|
+
family: HealthcareRoleFamily;
|
|
1912
|
+
codingSystem: string;
|
|
1913
|
+
code: string;
|
|
1914
|
+
claim: string;
|
|
1915
|
+
i18nKey: `org.ilo.isco-08.${string}` | `org.isco08.${string}` | `org.hl7.terminology.CodeSystem.v3-RoleCode.${string}`;
|
|
1916
|
+
titleEn: string;
|
|
1917
|
+
definition?: string;
|
|
1918
|
+
}>>>;
|
|
1880
1919
|
readonly "onehealth-tech": Readonly<Record<string, Readonly<{
|
|
1881
1920
|
family: HealthcareRoleFamily;
|
|
1882
1921
|
codingSystem: string;
|
|
@@ -359,7 +359,13 @@ 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',
|
|
367
|
+
/** General receptionist occupation usable across human, animal and one-health organizations. */
|
|
368
|
+
Receptionist: 'ISCO-08|4226',
|
|
363
369
|
/** Administrative health occupation; it does not imply clinical authority. */
|
|
364
370
|
MedicalSecretary: 'ISCO-08|3344',
|
|
365
371
|
});
|
|
@@ -382,7 +388,10 @@ export const HealthcareActorRoleCodes = Object.freeze({
|
|
|
382
388
|
MidwiferyProfessional: '2222',
|
|
383
389
|
Paramedic: '2240',
|
|
384
390
|
Veterinarian: '2250',
|
|
391
|
+
LifeScienceTechnician: '3141',
|
|
392
|
+
MedicalAndPathologyLaboratoryTechnician: '3212',
|
|
385
393
|
VeterinaryTechnicianOrAssistant: '3240',
|
|
394
|
+
Receptionist: '4226',
|
|
386
395
|
MedicalSecretary: '3344',
|
|
387
396
|
});
|
|
388
397
|
function buildProfessionalIscoRoles() {
|
|
@@ -464,6 +473,28 @@ function buildProfessionalIscoRoles() {
|
|
|
464
473
|
titleEn: 'Veterinarian',
|
|
465
474
|
}),
|
|
466
475
|
],
|
|
476
|
+
[
|
|
477
|
+
HealthcareActorRoleCodes.LifeScienceTechnician,
|
|
478
|
+
Object.freeze({
|
|
479
|
+
family: HealthcareRoleFamilies.ProfessionalOccupationIsco08,
|
|
480
|
+
codingSystem: ISCO08_CODING_SYSTEM,
|
|
481
|
+
code: HealthcareActorRoleCodes.LifeScienceTechnician,
|
|
482
|
+
claim: HealthcareActorRoles.LifeScienceTechnician,
|
|
483
|
+
i18nKey: `${ISCO08_I18N_NAMESPACE}.${HealthcareActorRoleCodes.LifeScienceTechnician}`,
|
|
484
|
+
titleEn: 'Life science technician (excluding medical)',
|
|
485
|
+
}),
|
|
486
|
+
],
|
|
487
|
+
[
|
|
488
|
+
HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician,
|
|
489
|
+
Object.freeze({
|
|
490
|
+
family: HealthcareRoleFamilies.ProfessionalOccupationIsco08,
|
|
491
|
+
codingSystem: ISCO08_CODING_SYSTEM,
|
|
492
|
+
code: HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician,
|
|
493
|
+
claim: HealthcareActorRoles.MedicalAndPathologyLaboratoryTechnician,
|
|
494
|
+
i18nKey: `${ISCO08_I18N_NAMESPACE}.${HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician}`,
|
|
495
|
+
titleEn: 'Medical and pathology laboratory technician',
|
|
496
|
+
}),
|
|
497
|
+
],
|
|
467
498
|
[
|
|
468
499
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
469
500
|
Object.freeze({
|
|
@@ -475,6 +506,17 @@ function buildProfessionalIscoRoles() {
|
|
|
475
506
|
titleEn: 'Veterinary technician or assistant',
|
|
476
507
|
}),
|
|
477
508
|
],
|
|
509
|
+
[
|
|
510
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
511
|
+
Object.freeze({
|
|
512
|
+
family: HealthcareRoleFamilies.ProfessionalOccupationIsco08,
|
|
513
|
+
codingSystem: ISCO08_CODING_SYSTEM,
|
|
514
|
+
code: HealthcareActorRoleCodes.Receptionist,
|
|
515
|
+
claim: HealthcareActorRoles.Receptionist,
|
|
516
|
+
i18nKey: `${ISCO08_I18N_NAMESPACE}.${HealthcareActorRoleCodes.Receptionist}`,
|
|
517
|
+
titleEn: 'Receptionists (general)',
|
|
518
|
+
}),
|
|
519
|
+
],
|
|
478
520
|
[
|
|
479
521
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
480
522
|
Object.freeze({
|
|
@@ -552,6 +594,7 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
552
594
|
HealthcareActorRoleCodes.NursingProfessional,
|
|
553
595
|
HealthcareActorRoleCodes.MidwiferyProfessional,
|
|
554
596
|
HealthcareActorRoleCodes.Paramedic,
|
|
597
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
555
598
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
556
599
|
]),
|
|
557
600
|
[DataspaceSectors.HealthResearch]: Object.freeze([
|
|
@@ -559,22 +602,40 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
559
602
|
HealthcareActorRoleCodes.MedicalDoctors,
|
|
560
603
|
HealthcareActorRoleCodes.GeneralistMedicalPractitioner,
|
|
561
604
|
HealthcareActorRoleCodes.SpecialistMedicalPractitioner,
|
|
605
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
562
606
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
563
607
|
]),
|
|
564
|
-
[DataspaceSectors.
|
|
565
|
-
|
|
608
|
+
[DataspaceSectors.HealthLab]: Object.freeze([
|
|
609
|
+
HealthcareActorRoleCodes.Controller,
|
|
610
|
+
HealthcareActorRoleCodes.MedicalDoctors,
|
|
611
|
+
HealthcareActorRoleCodes.SpecialistMedicalPractitioner,
|
|
612
|
+
HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician,
|
|
613
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
614
|
+
HealthcareActorRoleCodes.MedicalSecretary,
|
|
615
|
+
]),
|
|
616
|
+
[DataspaceSectors.HealthTech]: Object.freeze([HealthcareActorRoleCodes.Controller, HealthcareActorRoleCodes.Receptionist]),
|
|
617
|
+
[DataspaceSectors.HealthInsurance]: Object.freeze([HealthcareActorRoleCodes.Controller, HealthcareActorRoleCodes.Receptionist]),
|
|
566
618
|
[DataspaceSectors.AnimalCare]: Object.freeze([
|
|
567
619
|
HealthcareActorRoleCodes.Controller,
|
|
568
620
|
HealthcareActorRoleCodes.Veterinarian,
|
|
569
621
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
622
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
570
623
|
]),
|
|
571
624
|
[DataspaceSectors.AnimalResearch]: Object.freeze([
|
|
572
625
|
HealthcareActorRoleCodes.Controller,
|
|
573
626
|
HealthcareActorRoleCodes.Veterinarian,
|
|
574
627
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
628
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
575
629
|
]),
|
|
576
|
-
[DataspaceSectors.
|
|
577
|
-
|
|
630
|
+
[DataspaceSectors.AnimalLab]: Object.freeze([
|
|
631
|
+
HealthcareActorRoleCodes.Controller,
|
|
632
|
+
HealthcareActorRoleCodes.Veterinarian,
|
|
633
|
+
HealthcareActorRoleCodes.LifeScienceTechnician,
|
|
634
|
+
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
635
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
636
|
+
]),
|
|
637
|
+
[DataspaceSectors.AnimalInsurance]: Object.freeze([HealthcareActorRoleCodes.Controller, HealthcareActorRoleCodes.Receptionist]),
|
|
638
|
+
[DataspaceSectors.AnimalTech]: Object.freeze([HealthcareActorRoleCodes.Controller, HealthcareActorRoleCodes.Receptionist]),
|
|
578
639
|
[DataspaceSectors.OneHealthResearch]: Object.freeze([
|
|
579
640
|
HealthcareActorRoleCodes.Controller,
|
|
580
641
|
HealthcareActorRoleCodes.MedicalDoctors,
|
|
@@ -582,20 +643,35 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
582
643
|
HealthcareActorRoleCodes.SpecialistMedicalPractitioner,
|
|
583
644
|
HealthcareActorRoleCodes.Veterinarian,
|
|
584
645
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
646
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
647
|
+
HealthcareActorRoleCodes.MedicalSecretary,
|
|
648
|
+
]),
|
|
649
|
+
[DataspaceSectors.OneHealthLab]: Object.freeze([
|
|
650
|
+
HealthcareActorRoleCodes.Controller,
|
|
651
|
+
HealthcareActorRoleCodes.MedicalDoctors,
|
|
652
|
+
HealthcareActorRoleCodes.SpecialistMedicalPractitioner,
|
|
653
|
+
HealthcareActorRoleCodes.Veterinarian,
|
|
654
|
+
HealthcareActorRoleCodes.LifeScienceTechnician,
|
|
655
|
+
HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician,
|
|
656
|
+
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
657
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
585
658
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
586
659
|
]),
|
|
587
|
-
[DataspaceSectors.OneHealthTech]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
660
|
+
[DataspaceSectors.OneHealthTech]: Object.freeze([HealthcareActorRoleCodes.Controller, HealthcareActorRoleCodes.Receptionist]),
|
|
588
661
|
});
|
|
589
662
|
export const HealthcareProfessionalRolesBySector = Object.freeze({
|
|
590
663
|
[DataspaceSectors.HealthCare]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthCare]),
|
|
591
664
|
[DataspaceSectors.HealthResearch]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthResearch]),
|
|
665
|
+
[DataspaceSectors.HealthLab]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthLab]),
|
|
592
666
|
[DataspaceSectors.HealthTech]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthTech]),
|
|
593
667
|
[DataspaceSectors.HealthInsurance]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthInsurance]),
|
|
594
668
|
[DataspaceSectors.AnimalCare]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.AnimalCare]),
|
|
595
669
|
[DataspaceSectors.AnimalResearch]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.AnimalResearch]),
|
|
670
|
+
[DataspaceSectors.AnimalLab]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.AnimalLab]),
|
|
596
671
|
[DataspaceSectors.AnimalInsurance]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.AnimalInsurance]),
|
|
597
672
|
[DataspaceSectors.AnimalTech]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.AnimalTech]),
|
|
598
673
|
[DataspaceSectors.OneHealthResearch]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.OneHealthResearch]),
|
|
674
|
+
[DataspaceSectors.OneHealthLab]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.OneHealthLab]),
|
|
599
675
|
[DataspaceSectors.OneHealthTech]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.OneHealthTech]),
|
|
600
676
|
});
|
|
601
677
|
export const HealthcareProfessionalRolesBySectorAndClaim = Object.freeze(Object.fromEntries(Object.values(DataspaceSectors).map((sector) => [
|
|
@@ -614,7 +690,14 @@ export function getHealthcareRolesByFamily(family) {
|
|
|
614
690
|
return HealthcareRolesByFamily[family];
|
|
615
691
|
}
|
|
616
692
|
export function getHealthcareRoleByClaim(claim) {
|
|
617
|
-
|
|
693
|
+
const normalized = String(claim || '').trim();
|
|
694
|
+
const direct = HealthcareAllRolesByClaim[normalized];
|
|
695
|
+
if (direct)
|
|
696
|
+
return direct;
|
|
697
|
+
const fullHl7Prefix = `${HL7_CODING_SYSTEM_V3_ROLE_CODE}|`;
|
|
698
|
+
return normalized.startsWith(fullHl7Prefix)
|
|
699
|
+
? HealthcareAllRolesByClaim[`v3-RoleCode|${normalized.slice(fullHl7Prefix.length)}`]
|
|
700
|
+
: undefined;
|
|
618
701
|
}
|
|
619
702
|
export function getHealthcareProfessionalRolesBySector(sector) {
|
|
620
703
|
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
|
+
}
|
|
@@ -33,10 +33,11 @@ export type FhirIpsCreatorAuthor = Readonly<{
|
|
|
33
33
|
}>;
|
|
34
34
|
export type FhirIpsCreatorProvenanceInput = FhirIpsCreatorAuthorInput & Readonly<{
|
|
35
35
|
/**
|
|
36
|
-
* Explicit source
|
|
37
|
-
* individual.
|
|
38
|
-
* the
|
|
39
|
-
*
|
|
36
|
+
* Explicit document/source author. Defaults to the owning organization or
|
|
37
|
+
* individual. A registered RelatedPerson/PractitionerRole is an attester,
|
|
38
|
+
* never a substitute for the organization, EHR/portal or individual that
|
|
39
|
+
* authors the document. Passing the assignment reference is normalized to
|
|
40
|
+
* the owner for compatibility with the former creator-as-author contract.
|
|
40
41
|
*/
|
|
41
42
|
compositionAuthorReference?: string;
|
|
42
43
|
/** Defaults to professional for employees and personal for individual members. */
|
|
@@ -58,13 +59,11 @@ export type FhirIpsCreatorProvenance = Readonly<{
|
|
|
58
59
|
/**
|
|
59
60
|
* Builds source authorship separately from the person or role that attests it.
|
|
60
61
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* intentionally reference the same assignment; neither is the transport
|
|
67
|
-
* sender or signing key.
|
|
62
|
+
* An organization's employee attests an organization/EHR-authored record,
|
|
63
|
+
* while a controller/member/caregiver attests an individual-authored record.
|
|
64
|
+
* Neither the attester nor the document author is the transport sender or
|
|
65
|
+
* signing key merely because the same authenticated person performed the
|
|
66
|
+
* submission.
|
|
68
67
|
*/
|
|
69
68
|
export declare function buildFhirIpsCreatorProvenance(input: FhirIpsCreatorProvenanceInput): FhirIpsCreatorProvenance;
|
|
70
69
|
/**
|
|
@@ -11,13 +11,11 @@ export const FhirIpsCreatorKinds = Object.freeze({
|
|
|
11
11
|
/**
|
|
12
12
|
* Builds source authorship separately from the person or role that attests it.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* intentionally reference the same assignment; neither is the transport
|
|
20
|
-
* sender or signing key.
|
|
14
|
+
* An organization's employee attests an organization/EHR-authored record,
|
|
15
|
+
* while a controller/member/caregiver attests an individual-authored record.
|
|
16
|
+
* Neither the attester nor the document author is the transport sender or
|
|
17
|
+
* signing key merely because the same authenticated person performed the
|
|
18
|
+
* submission.
|
|
21
19
|
*/
|
|
22
20
|
export function buildFhirIpsCreatorProvenance(input) {
|
|
23
21
|
if (input.kind === FhirIpsCreatorKinds.IndividualSubject) {
|
|
@@ -31,11 +29,14 @@ export function buildFhirIpsCreatorProvenance(input) {
|
|
|
31
29
|
const defaultAuthor = input.kind === FhirIpsCreatorKinds.Professional
|
|
32
30
|
? input.organizationReference
|
|
33
31
|
: input.subjectReference;
|
|
32
|
+
const requestedAuthor = input.compositionAuthorReference === creator.authorReference
|
|
33
|
+
? defaultAuthor
|
|
34
|
+
: input.compositionAuthorReference;
|
|
34
35
|
const mode = input.attesterMode || (input.kind === FhirIpsCreatorKinds.Professional
|
|
35
36
|
? CompositionAttesterModes.Professional
|
|
36
37
|
: CompositionAttesterModes.Personal);
|
|
37
38
|
return {
|
|
38
|
-
authorReference: requireReference(
|
|
39
|
+
authorReference: requireReference(requestedAuthor || defaultAuthor, 'compositionAuthorReference'),
|
|
39
40
|
attesters: [{
|
|
40
41
|
mode,
|
|
41
42
|
party: { reference: creator.authorReference },
|
|
@@ -89,6 +90,13 @@ export function buildFhirIpsCreatorAuthor(input) {
|
|
|
89
90
|
return {
|
|
90
91
|
authorReference: authorIdentifier,
|
|
91
92
|
entries: [
|
|
93
|
+
{
|
|
94
|
+
fullUrl: organizationReference,
|
|
95
|
+
resource: {
|
|
96
|
+
resourceType: 'Organization',
|
|
97
|
+
identifier: [{ value: organizationReference }],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
92
100
|
{
|
|
93
101
|
fullUrl: actorIdentifier,
|
|
94
102
|
resource: {
|