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 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
- return HealthcareAllRolesByClaim[String(claim || '').trim()];
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";
@@ -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,
@@ -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 supported forms:
193
- * - hosted:
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
- * - `individualId` must be the canonical stable identity of the individual
200
- * - `individualId` should already be serialized in multibase form, typically
201
- * `zBase58(UUID-bytes)`
202
- * - role is not part of the canonical individual DID; roles are only appended
203
- * in member DIDs via `:member:role:<role-code-no-coding-system>`
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:multibase:<individualId>:member:role:<role-code-no-coding-system>`
232
+ * `did:web:...:individual:<type>:<secure-value>:member:<secure-member-value>:<role-value>`
218
233
  *
219
- * The DID suffix intentionally strips coding-system prefixes because the DID is
220
- * a compact routing/identity string, while the full coded role still belongs in
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
- role: string;
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 supported forms:
338
- * - hosted:
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
- * - `individualId` must be the canonical stable identity of the individual
345
- * - `individualId` should already be serialized in multibase form, typically
346
- * `zBase58(UUID-bytes)`
347
- * - role is not part of the canonical individual DID; roles are only appended
348
- * in member DIDs via `:member:role:<role-code-no-coding-system>`
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:multibase:<individualId>:member:role:<role-code-no-coding-system>`
444
+ * `did:web:...:individual:<type>:<secure-value>:member:<secure-member-value>:<role-value>`
372
445
  *
373
- * The DID suffix intentionally strips coding-system prefixes because the DID is
374
- * a compact routing/identity string, while the full coded role still belongs in
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "2.8.8",
3
+ "version": "2.9.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },