gdc-common-utils-ts 2.9.0 → 2.9.2
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 +10 -3
- package/dist/constants/healthcare.d.ts +16 -13
- package/dist/constants/healthcare.js +27 -5
- package/dist/examples/ica-verify-response.d.ts +3 -3
- package/dist/examples/shared.d.ts +5 -1
- package/dist/examples/shared.js +20 -2
- package/dist/utils/fhir-ips-creator-identity.d.ts +10 -11
- package/dist/utils/fhir-ips-creator-identity.js +33 -14
- package/dist/utils/organization-authorization-urn.d.ts +39 -0
- package/dist/utils/organization-authorization-urn.js +52 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -458,6 +458,13 @@ The canonical API contract should live in JSDoc on exported code. The README act
|
|
|
458
458
|
- Uses explicit `seedMaterial` for deterministic derivation. Without `seedMaterial`, it defaults to random generation. `mode = deterministic` requires `seedMaterial`.
|
|
459
459
|
- [`buildOrganizationDidWeb(...)`, `buildProfessionalDidWeb(...)`, `buildIndividualDidWeb(...)`, `buildIndividualMemberDidWebFromPrivateIdentifiers(...)`](src/utils/did.ts)
|
|
460
460
|
- Build canonical data-space `did:web` identifiers for hosted organizations, professionals, and individuals/family actors.
|
|
461
|
+
- [`buildOrganizationAuthorizationUrnCds(...)`, `buildOrganizationMemberAuthorizationUrnCds(...)`](src/utils/organization-authorization-urn.ts)
|
|
462
|
+
- Build host-independent legal organization/member identities such as
|
|
463
|
+
`urn:cds-es:v1:organization:tax:ES-B00112233` and its
|
|
464
|
+
`:member:<multibase-id>:<role-value>` child.
|
|
465
|
+
- Jurisdiction is mandatory so `BN`, `EIN`, `TAX` and subnational identifier
|
|
466
|
+
schemes cannot collide; `roleType` remains protected while only the compact
|
|
467
|
+
`roleValue` is serialized.
|
|
461
468
|
- [`buildSmartCompositionReadScope(...)`](src/utils/smart-scope.ts)
|
|
462
469
|
- Builds the current CORE GW pinned SMART root scope for `organization/Composition...` token requests.
|
|
463
470
|
- This is the preferred first scope to teach when the backend only needs subject-scoped read access.
|
|
@@ -530,9 +537,9 @@ The canonical API contract should live in JSDoc on exported code. The README act
|
|
|
530
537
|
- [`buildFhirIpsCreatorProvenance(...)`](src/utils/fhir-ips-creator-identity.ts)
|
|
531
538
|
and [`docs/101-DIDCOMM-IDENTITY-BOUNDARY.md`](docs/101-DIDCOMM-IDENTITY-BOUNDARY.md)
|
|
532
539
|
- Keep `Composition.author`, `Composition.attester`, DIDComm sender and
|
|
533
|
-
signing key separate.
|
|
534
|
-
|
|
535
|
-
|
|
540
|
+
signing key separate. The organization, EHR/portal or individual remains
|
|
541
|
+
the document author; a registered PractitionerRole or RelatedPerson is the
|
|
542
|
+
attester, including for content entered by that authenticated person.
|
|
536
543
|
- [`src/examples/frontend-session.ts`](src/examples/frontend-session.ts)
|
|
537
544
|
- Frontend profile/session bootstrap examples.
|
|
538
545
|
- [`src/examples/lifecycle.ts`](src/examples/lifecycle.ts)
|
|
@@ -1732,6 +1732,8 @@ export declare const HealthcareActorRoles: Readonly<{
|
|
|
1732
1732
|
/** ISCO-08 laboratory occupation for human medical/pathology work. */
|
|
1733
1733
|
readonly MedicalAndPathologyLaboratoryTechnician: "ISCO-08|3212";
|
|
1734
1734
|
readonly VeterinaryTechnicianOrAssistant: "ISCO-08|3240";
|
|
1735
|
+
/** General receptionist occupation usable across human, animal and one-health organizations. */
|
|
1736
|
+
readonly Receptionist: "ISCO-08|4226";
|
|
1735
1737
|
/** Administrative health occupation; it does not imply clinical authority. */
|
|
1736
1738
|
readonly MedicalSecretary: "ISCO-08|3344";
|
|
1737
1739
|
}>;
|
|
@@ -1757,6 +1759,7 @@ export declare const HealthcareActorRoleCodes: Readonly<{
|
|
|
1757
1759
|
readonly LifeScienceTechnician: "3141";
|
|
1758
1760
|
readonly MedicalAndPathologyLaboratoryTechnician: "3212";
|
|
1759
1761
|
readonly VeterinaryTechnicianOrAssistant: "3240";
|
|
1762
|
+
readonly Receptionist: "4226";
|
|
1760
1763
|
readonly MedicalSecretary: "3344";
|
|
1761
1764
|
}>;
|
|
1762
1765
|
export declare const HealthcareRolesByFamily: Readonly<{
|
|
@@ -1790,19 +1793,19 @@ export declare const HealthcareRolesByFamily: Readonly<{
|
|
|
1790
1793
|
}>;
|
|
1791
1794
|
export declare const HealthcareAllRolesByClaim: Readonly<Record<string, HealthcareActorRoleDescriptor>>;
|
|
1792
1795
|
export declare const HealthcareProfessionalRoleCodesBySector: Readonly<{
|
|
1793
|
-
readonly "health-care": readonly ("RESPRSN" | "221" | "2211" | "2212" | "2221" | "2222" | "2240" | "3344")[];
|
|
1794
|
-
readonly "health-research": readonly ("RESPRSN" | "221" | "2211" | "2212" | "3344")[];
|
|
1795
|
-
readonly "health-lab": readonly ("RESPRSN" | "221" | "2212" | "3212" | "3344")[];
|
|
1796
|
-
readonly "health-tech": readonly "RESPRSN"[];
|
|
1797
|
-
readonly "health-insurance": readonly "RESPRSN"[];
|
|
1798
|
-
readonly "animal-care": readonly ("RESPRSN" | "2250" | "3240")[];
|
|
1799
|
-
readonly "animal-research": readonly ("RESPRSN" | "2250" | "3240")[];
|
|
1800
|
-
readonly "animal-lab": readonly ("RESPRSN" | "2250" | "3141" | "3240")[];
|
|
1801
|
-
readonly "animal-insurance": readonly "RESPRSN"[];
|
|
1802
|
-
readonly "animal-tech": readonly "RESPRSN"[];
|
|
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")[];
|
|
1805
|
-
readonly "onehealth-tech": readonly "RESPRSN"[];
|
|
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")[];
|
|
1806
1809
|
}>;
|
|
1807
1810
|
export declare const HealthcareProfessionalRolesBySector: Readonly<{
|
|
1808
1811
|
readonly "health-care": Readonly<Record<string, Readonly<{
|
|
@@ -364,6 +364,8 @@ export const HealthcareActorRoles = Object.freeze({
|
|
|
364
364
|
/** ISCO-08 laboratory occupation for human medical/pathology work. */
|
|
365
365
|
MedicalAndPathologyLaboratoryTechnician: 'ISCO-08|3212',
|
|
366
366
|
VeterinaryTechnicianOrAssistant: 'ISCO-08|3240',
|
|
367
|
+
/** General receptionist occupation usable across human, animal and one-health organizations. */
|
|
368
|
+
Receptionist: 'ISCO-08|4226',
|
|
367
369
|
/** Administrative health occupation; it does not imply clinical authority. */
|
|
368
370
|
MedicalSecretary: 'ISCO-08|3344',
|
|
369
371
|
});
|
|
@@ -389,6 +391,7 @@ export const HealthcareActorRoleCodes = Object.freeze({
|
|
|
389
391
|
LifeScienceTechnician: '3141',
|
|
390
392
|
MedicalAndPathologyLaboratoryTechnician: '3212',
|
|
391
393
|
VeterinaryTechnicianOrAssistant: '3240',
|
|
394
|
+
Receptionist: '4226',
|
|
392
395
|
MedicalSecretary: '3344',
|
|
393
396
|
});
|
|
394
397
|
function buildProfessionalIscoRoles() {
|
|
@@ -503,6 +506,17 @@ function buildProfessionalIscoRoles() {
|
|
|
503
506
|
titleEn: 'Veterinary technician or assistant',
|
|
504
507
|
}),
|
|
505
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
|
+
],
|
|
506
520
|
[
|
|
507
521
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
508
522
|
Object.freeze({
|
|
@@ -580,6 +594,7 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
580
594
|
HealthcareActorRoleCodes.NursingProfessional,
|
|
581
595
|
HealthcareActorRoleCodes.MidwiferyProfessional,
|
|
582
596
|
HealthcareActorRoleCodes.Paramedic,
|
|
597
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
583
598
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
584
599
|
]),
|
|
585
600
|
[DataspaceSectors.HealthResearch]: Object.freeze([
|
|
@@ -587,6 +602,7 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
587
602
|
HealthcareActorRoleCodes.MedicalDoctors,
|
|
588
603
|
HealthcareActorRoleCodes.GeneralistMedicalPractitioner,
|
|
589
604
|
HealthcareActorRoleCodes.SpecialistMedicalPractitioner,
|
|
605
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
590
606
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
591
607
|
]),
|
|
592
608
|
[DataspaceSectors.HealthLab]: Object.freeze([
|
|
@@ -594,28 +610,32 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
594
610
|
HealthcareActorRoleCodes.MedicalDoctors,
|
|
595
611
|
HealthcareActorRoleCodes.SpecialistMedicalPractitioner,
|
|
596
612
|
HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician,
|
|
613
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
597
614
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
598
615
|
]),
|
|
599
|
-
[DataspaceSectors.HealthTech]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
600
|
-
[DataspaceSectors.HealthInsurance]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
616
|
+
[DataspaceSectors.HealthTech]: Object.freeze([HealthcareActorRoleCodes.Controller, HealthcareActorRoleCodes.Receptionist]),
|
|
617
|
+
[DataspaceSectors.HealthInsurance]: Object.freeze([HealthcareActorRoleCodes.Controller, HealthcareActorRoleCodes.Receptionist]),
|
|
601
618
|
[DataspaceSectors.AnimalCare]: Object.freeze([
|
|
602
619
|
HealthcareActorRoleCodes.Controller,
|
|
603
620
|
HealthcareActorRoleCodes.Veterinarian,
|
|
604
621
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
622
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
605
623
|
]),
|
|
606
624
|
[DataspaceSectors.AnimalResearch]: Object.freeze([
|
|
607
625
|
HealthcareActorRoleCodes.Controller,
|
|
608
626
|
HealthcareActorRoleCodes.Veterinarian,
|
|
609
627
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
628
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
610
629
|
]),
|
|
611
630
|
[DataspaceSectors.AnimalLab]: Object.freeze([
|
|
612
631
|
HealthcareActorRoleCodes.Controller,
|
|
613
632
|
HealthcareActorRoleCodes.Veterinarian,
|
|
614
633
|
HealthcareActorRoleCodes.LifeScienceTechnician,
|
|
615
634
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
635
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
616
636
|
]),
|
|
617
|
-
[DataspaceSectors.AnimalInsurance]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
618
|
-
[DataspaceSectors.AnimalTech]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
637
|
+
[DataspaceSectors.AnimalInsurance]: Object.freeze([HealthcareActorRoleCodes.Controller, HealthcareActorRoleCodes.Receptionist]),
|
|
638
|
+
[DataspaceSectors.AnimalTech]: Object.freeze([HealthcareActorRoleCodes.Controller, HealthcareActorRoleCodes.Receptionist]),
|
|
619
639
|
[DataspaceSectors.OneHealthResearch]: Object.freeze([
|
|
620
640
|
HealthcareActorRoleCodes.Controller,
|
|
621
641
|
HealthcareActorRoleCodes.MedicalDoctors,
|
|
@@ -623,6 +643,7 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
623
643
|
HealthcareActorRoleCodes.SpecialistMedicalPractitioner,
|
|
624
644
|
HealthcareActorRoleCodes.Veterinarian,
|
|
625
645
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
646
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
626
647
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
627
648
|
]),
|
|
628
649
|
[DataspaceSectors.OneHealthLab]: Object.freeze([
|
|
@@ -633,9 +654,10 @@ export const HealthcareProfessionalRoleCodesBySector = Object.freeze({
|
|
|
633
654
|
HealthcareActorRoleCodes.LifeScienceTechnician,
|
|
634
655
|
HealthcareActorRoleCodes.MedicalAndPathologyLaboratoryTechnician,
|
|
635
656
|
HealthcareActorRoleCodes.VeterinaryTechnicianOrAssistant,
|
|
657
|
+
HealthcareActorRoleCodes.Receptionist,
|
|
636
658
|
HealthcareActorRoleCodes.MedicalSecretary,
|
|
637
659
|
]),
|
|
638
|
-
[DataspaceSectors.OneHealthTech]: Object.freeze([HealthcareActorRoleCodes.Controller]),
|
|
660
|
+
[DataspaceSectors.OneHealthTech]: Object.freeze([HealthcareActorRoleCodes.Controller, HealthcareActorRoleCodes.Receptionist]),
|
|
639
661
|
});
|
|
640
662
|
export const HealthcareProfessionalRolesBySector = Object.freeze({
|
|
641
663
|
[DataspaceSectors.HealthCare]: pickRoleCatalogByCodes(HealthcareProfessionalAndControllerRoles, HealthcareProfessionalRoleCodesBySector[DataspaceSectors.HealthCare]),
|
|
@@ -159,7 +159,7 @@ export declare const EXAMPLE_VERIFY_RESPONSE_ORG_CREDENTIAL: Readonly<{
|
|
|
159
159
|
id: "did:web:globaldatacare.es:onehealth:organization:taxid:VATES-B00112233";
|
|
160
160
|
'@type': "Organization";
|
|
161
161
|
legalName: "ACME Health Provider";
|
|
162
|
-
taxID: "
|
|
162
|
+
taxID: "ES-B00112233";
|
|
163
163
|
sameAs: "did:web:provider.example.org";
|
|
164
164
|
url: "health-care.provider.example.org";
|
|
165
165
|
alternateName: string;
|
|
@@ -210,7 +210,7 @@ export declare const EXAMPLE_VERIFY_RESPONSE_PERSON_CREDENTIAL: Readonly<{
|
|
|
210
210
|
memberOf: {
|
|
211
211
|
'@type': "Organization";
|
|
212
212
|
legalName: "ACME Health Provider";
|
|
213
|
-
taxID: "
|
|
213
|
+
taxID: "ES-B00112233";
|
|
214
214
|
};
|
|
215
215
|
alternateName: "controller-es384-001";
|
|
216
216
|
additionalType: "ES384";
|
|
@@ -240,7 +240,7 @@ export declare const EXAMPLE_VERIFY_RESPONSE_CONTROLLER_CREDENTIAL: Readonly<{
|
|
|
240
240
|
provider: {
|
|
241
241
|
'@type': "Organization";
|
|
242
242
|
legalName: "ACME Health Provider";
|
|
243
|
-
taxID: "
|
|
243
|
+
taxID: "ES-B00112233";
|
|
244
244
|
};
|
|
245
245
|
owner: {
|
|
246
246
|
'@type': "Person";
|
|
@@ -144,7 +144,10 @@ export declare const EXAMPLE_HOST_PUBLIC_HOSTNAME: "host.example.com";
|
|
|
144
144
|
export declare const EXAMPLE_HOST_PUBLIC_DID: "did:web:host.example.com";
|
|
145
145
|
export declare const EXAMPLE_PERSON_CARD_URI: "urn:example:card:person:subject-001";
|
|
146
146
|
export declare const EXAMPLE_ANIMAL_CARD_URI: "urn:example:card:animal:subject-002";
|
|
147
|
-
export declare const EXAMPLE_PROVIDER_TAX_ID: "
|
|
147
|
+
export declare const EXAMPLE_PROVIDER_TAX_ID: "ES-B00112233";
|
|
148
|
+
/** Jurisdiction-scoped synthetic legal identifier used by stable CDS URNs. */
|
|
149
|
+
export declare const EXAMPLE_PROVIDER_LEGAL_IDENTIFIER_VALUE_CDS: "ES-B00112233";
|
|
150
|
+
export declare const EXAMPLE_PROVIDER_ORGANIZATION_AUTHORIZATION_URN_CDS: string;
|
|
148
151
|
export declare const EXAMPLE_PROVIDER_DOMAIN: "health-care.provider.example.org";
|
|
149
152
|
/** Synthetic public portal domain used to derive organization path DIDs. */
|
|
150
153
|
export declare const EXAMPLE_PUBLIC_PORTAL_DOMAIN: "portal.example.org";
|
|
@@ -216,6 +219,7 @@ export declare const EXAMPLE_HEALTHCARE_ACTOR_ROLE_RECEPTIONIST: "ISCO-08|4226";
|
|
|
216
219
|
export declare const EXAMPLE_HEALTHCARE_ROLE_PHYSICIAN_TEXT: "physician";
|
|
217
220
|
export declare const EXAMPLE_INDIVIDUAL_CONTROLLER_ROLE_TYPE = "http://terminology.hl7.org/CodeSystem/v3-RoleCode";
|
|
218
221
|
export declare const EXAMPLE_INDIVIDUAL_CONTROLLER_ROLE_VALUE: "RESPRSN";
|
|
222
|
+
export declare const EXAMPLE_PROFESSIONAL_AUTHORIZATION_URN_CDS: string;
|
|
219
223
|
export declare const EXAMPLE_RELATED_PERSON_ROLE: "v3-RoleCode|RESPRSN";
|
|
220
224
|
export declare const EXAMPLE_SECURE_ID_VALUE_INDIVIDUAL: string;
|
|
221
225
|
export declare const EXAMPLE_SECURE_ID_VALUE_INDIVIDUAL_CONTROLLER: string;
|
package/dist/examples/shared.js
CHANGED
|
@@ -8,12 +8,13 @@ import { ResourceTypesFhirR4 } from '../constants/fhir-resource-types.js';
|
|
|
8
8
|
import { BirthSex, GenderIdentity } from '../constants/identity-gender.js';
|
|
9
9
|
import { IdKind, SecureIdTypesIndividual } from '../constants/identity-identifiers.js';
|
|
10
10
|
import { HL7_CODING_SYSTEM_V3_ROLE_CODE } from '../constants/hl7-roles.js';
|
|
11
|
-
import { HealthcareActorRoles, HealthcareBasicSections, HealthcareConsentPurposes, HealthcareDocumentTypes, } from '../constants/healthcare.js';
|
|
11
|
+
import { HealthcareActorRoles, HealthcareBasicSections, HealthcareConsentPurposes, HealthcareDocumentTypes, ISCO08_CODING_SYSTEM, } from '../constants/healthcare.js';
|
|
12
12
|
import { CommunicationCategoryCodes } from '../constants/communication.js';
|
|
13
13
|
import { LOINC_SYSTEM_URL } from '../models/clinical-sections.js';
|
|
14
14
|
import { CommunicationClaim } from '../models/interoperable-claims/communication-claims.js';
|
|
15
15
|
import { buildHostedProviderDidWeb, buildIndividualDidWeb, buildIndividualMemberDidWeb, buildSecureIdValueIndividual, buildSecureIdValueMember, buildProfessionalDidWeb, buildProviderSectorDidWeb, } from '../utils/did.js';
|
|
16
16
|
import { encodeHexToMultibase58btc } from '../utils/multibase58.js';
|
|
17
|
+
import { buildOrganizationAuthorizationUrnCds, buildOrganizationMemberAuthorizationUrnCds, } from '../utils/organization-authorization-urn.js';
|
|
17
18
|
import { MedicationStatementClaim, MedicationStatementClaimsFhirApiExtended, } from '../models/interoperable-claims/medication-statement-claims.js';
|
|
18
19
|
import { medicationStatementFlatToFhirR4 } from '../utils/clinical-resource-converters.js';
|
|
19
20
|
/**
|
|
@@ -163,7 +164,15 @@ export const EXAMPLE_HOST_PUBLIC_HOSTNAME = 'host.example.com';
|
|
|
163
164
|
export const EXAMPLE_HOST_PUBLIC_DID = `did:web:${EXAMPLE_HOST_PUBLIC_HOSTNAME}`;
|
|
164
165
|
export const EXAMPLE_PERSON_CARD_URI = 'urn:example:card:person:subject-001';
|
|
165
166
|
export const EXAMPLE_ANIMAL_CARD_URI = 'urn:example:card:animal:subject-002';
|
|
166
|
-
export const EXAMPLE_PROVIDER_TAX_ID = '
|
|
167
|
+
export const EXAMPLE_PROVIDER_TAX_ID = 'ES-B00112233';
|
|
168
|
+
/** Jurisdiction-scoped synthetic legal identifier used by stable CDS URNs. */
|
|
169
|
+
export const EXAMPLE_PROVIDER_LEGAL_IDENTIFIER_VALUE_CDS = 'ES-B00112233';
|
|
170
|
+
export const EXAMPLE_PROVIDER_ORGANIZATION_AUTHORIZATION_URN_CDS = buildOrganizationAuthorizationUrnCds({
|
|
171
|
+
jurisdiction: EXAMPLE_JURISDICTION,
|
|
172
|
+
version: EXAMPLE_ROUTE_VERSION,
|
|
173
|
+
identifierType: 'TAX',
|
|
174
|
+
identifierValue: EXAMPLE_PROVIDER_LEGAL_IDENTIFIER_VALUE_CDS,
|
|
175
|
+
});
|
|
167
176
|
export const EXAMPLE_PROVIDER_DOMAIN = 'health-care.provider.example.org';
|
|
168
177
|
/** Synthetic public portal domain used to derive organization path DIDs. */
|
|
169
178
|
export const EXAMPLE_PUBLIC_PORTAL_DOMAIN = 'portal.example.org';
|
|
@@ -256,6 +265,15 @@ export const EXAMPLE_HEALTHCARE_ACTOR_ROLE_RECEPTIONIST = 'ISCO-08|4226';
|
|
|
256
265
|
export const EXAMPLE_HEALTHCARE_ROLE_PHYSICIAN_TEXT = 'physician';
|
|
257
266
|
export const EXAMPLE_INDIVIDUAL_CONTROLLER_ROLE_TYPE = HL7_CODING_SYSTEM_V3_ROLE_CODE;
|
|
258
267
|
export const EXAMPLE_INDIVIDUAL_CONTROLLER_ROLE_VALUE = EXAMPLE_ORGANIZATION_CONTROLLER_ROLE;
|
|
268
|
+
export const EXAMPLE_PROFESSIONAL_AUTHORIZATION_URN_CDS = buildOrganizationMemberAuthorizationUrnCds({
|
|
269
|
+
organizationUrn: EXAMPLE_PROVIDER_ORGANIZATION_AUTHORIZATION_URN_CDS,
|
|
270
|
+
memberId: buildSecureIdValueMember({
|
|
271
|
+
secureIdTypeMember: SecureIdTypesIndividual.Email,
|
|
272
|
+
privateIdValueMember: EXAMPLE_EMAIL_PROFESSIONAL,
|
|
273
|
+
}),
|
|
274
|
+
roleType: ISCO08_CODING_SYSTEM,
|
|
275
|
+
roleValue: HealthcareActorRoles.GeneralistMedicalPractitioner,
|
|
276
|
+
});
|
|
259
277
|
export const EXAMPLE_RELATED_PERSON_ROLE = 'v3-RoleCode|RESPRSN';
|
|
260
278
|
export const EXAMPLE_SECURE_ID_VALUE_INDIVIDUAL = buildSecureIdValueIndividual({
|
|
261
279
|
secureIdTypeIndividual: SecureIdTypesIndividual.Uuid,
|
|
@@ -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
|
/**
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { getHealthcareRoleByClaim } from '../constants/healthcare.js';
|
|
3
3
|
import { isStableActorIdentifier } from './actor-identifier.js';
|
|
4
4
|
import { normalizeUuid } from './normalize-uuid.js';
|
|
5
|
+
import { HL7_RELATED_PERSON_FUNCTIONAL_ROLES } from '../constants/hl7-roles.js';
|
|
5
6
|
import { CompositionAttesterModes, } from '../models/interoperable-claims/composition-claims.js';
|
|
6
7
|
export const FhirIpsCreatorKinds = Object.freeze({
|
|
7
8
|
IndividualSubject: 'individual-subject',
|
|
@@ -11,13 +12,11 @@ export const FhirIpsCreatorKinds = Object.freeze({
|
|
|
11
12
|
/**
|
|
12
13
|
* Builds source authorship separately from the person or role that attests it.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* intentionally reference the same assignment; neither is the transport
|
|
20
|
-
* sender or signing key.
|
|
15
|
+
* An organization's employee attests an organization/EHR-authored record,
|
|
16
|
+
* while a controller/member/caregiver attests an individual-authored record.
|
|
17
|
+
* Neither the attester nor the document author is the transport sender or
|
|
18
|
+
* signing key merely because the same authenticated person performed the
|
|
19
|
+
* submission.
|
|
21
20
|
*/
|
|
22
21
|
export function buildFhirIpsCreatorProvenance(input) {
|
|
23
22
|
if (input.kind === FhirIpsCreatorKinds.IndividualSubject) {
|
|
@@ -31,11 +30,14 @@ export function buildFhirIpsCreatorProvenance(input) {
|
|
|
31
30
|
const defaultAuthor = input.kind === FhirIpsCreatorKinds.Professional
|
|
32
31
|
? input.organizationReference
|
|
33
32
|
: input.subjectReference;
|
|
33
|
+
const requestedAuthor = input.compositionAuthorReference === creator.authorReference
|
|
34
|
+
? defaultAuthor
|
|
35
|
+
: input.compositionAuthorReference;
|
|
34
36
|
const mode = input.attesterMode || (input.kind === FhirIpsCreatorKinds.Professional
|
|
35
37
|
? CompositionAttesterModes.Professional
|
|
36
38
|
: CompositionAttesterModes.Personal);
|
|
37
39
|
return {
|
|
38
|
-
authorReference: requireReference(
|
|
40
|
+
authorReference: requireReference(requestedAuthor || defaultAuthor, 'compositionAuthorReference'),
|
|
39
41
|
attesters: [{
|
|
40
42
|
mode,
|
|
41
43
|
party: { reference: creator.authorReference },
|
|
@@ -89,6 +91,13 @@ export function buildFhirIpsCreatorAuthor(input) {
|
|
|
89
91
|
return {
|
|
90
92
|
authorReference: authorIdentifier,
|
|
91
93
|
entries: [
|
|
94
|
+
{
|
|
95
|
+
fullUrl: organizationReference,
|
|
96
|
+
resource: {
|
|
97
|
+
resourceType: 'Organization',
|
|
98
|
+
identifier: [{ value: organizationReference }],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
92
101
|
{
|
|
93
102
|
fullUrl: actorIdentifier,
|
|
94
103
|
resource: {
|
|
@@ -163,12 +172,22 @@ function requireReference(value, label) {
|
|
|
163
172
|
return normalized;
|
|
164
173
|
}
|
|
165
174
|
function requireRoleCoding(roleClaim) {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
175
|
+
const normalizedRole = String(roleClaim || '').trim();
|
|
176
|
+
const descriptor = getHealthcareRoleByClaim(normalizedRole);
|
|
177
|
+
if (descriptor) {
|
|
178
|
+
return {
|
|
179
|
+
system: descriptor.codingSystem,
|
|
180
|
+
code: descriptor.code,
|
|
181
|
+
...(descriptor.titleEn ? { display: descriptor.titleEn } : {}),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const functionalRole = HL7_RELATED_PERSON_FUNCTIONAL_ROLES.find((candidate) => normalizedRole === `${candidate.codingSystem}|${candidate.code}`);
|
|
185
|
+
if (!functionalRole) {
|
|
186
|
+
throw new TypeError('role must be a canonical governed healthcare or RelatedPerson functional role.');
|
|
187
|
+
}
|
|
169
188
|
return {
|
|
170
|
-
system:
|
|
171
|
-
code:
|
|
172
|
-
|
|
189
|
+
system: functionalRole.codingSystem,
|
|
190
|
+
code: functionalRole.code,
|
|
191
|
+
display: functionalRole.display,
|
|
173
192
|
};
|
|
174
193
|
}
|
|
@@ -3,6 +3,25 @@ export type OrganizationAuthorizationUrnInput = Readonly<{
|
|
|
3
3
|
identifierType: string;
|
|
4
4
|
identifierValue: string;
|
|
5
5
|
}>;
|
|
6
|
+
export type OrganizationAuthorizationUrnCdsInput = Readonly<{
|
|
7
|
+
/** ISO country or governed subdivision such as ES, US, US-WA or CA-BC. */
|
|
8
|
+
jurisdiction: string;
|
|
9
|
+
/** Version of the stable CDS identifier grammar. Defaults to v1. */
|
|
10
|
+
version?: string;
|
|
11
|
+
/** Governed legal identifier type such as TAX, EIN or BN. */
|
|
12
|
+
identifierType: string;
|
|
13
|
+
/** Canonical value issued by that jurisdiction's identifier authority. */
|
|
14
|
+
identifierValue: string;
|
|
15
|
+
}>;
|
|
16
|
+
export type OrganizationMemberAuthorizationUrnCdsInput = Readonly<{
|
|
17
|
+
organizationUrn: string;
|
|
18
|
+
/** Bare base58btc multibase identifier (`z...`); never raw email/phone. */
|
|
19
|
+
memberId: string;
|
|
20
|
+
/** Required protected coding system; deliberately not serialized. */
|
|
21
|
+
roleType: string;
|
|
22
|
+
/** Coded role value serialized as the final compact path segment. */
|
|
23
|
+
roleValue: string;
|
|
24
|
+
}>;
|
|
6
25
|
export type MemberAuthorizationUrnInput = Readonly<{
|
|
7
26
|
organizationUrn?: string;
|
|
8
27
|
identifierType?: string;
|
|
@@ -15,6 +34,10 @@ export type MemberAuthorizationUrnInput = Readonly<{
|
|
|
15
34
|
* Canonical organization authorization identifier used by ledger, consent-like
|
|
16
35
|
* rules, and inter-tenant authorization persistence.
|
|
17
36
|
*
|
|
37
|
+
* @deprecated For new cross-jurisdiction identities use
|
|
38
|
+
* `buildOrganizationAuthorizationUrnCds`, which requires jurisdiction and
|
|
39
|
+
* grammar version. This legacy form remains readable during migration.
|
|
40
|
+
*
|
|
18
41
|
* Format:
|
|
19
42
|
* - `urn:org:<identifierType-lowercase>:<identifierValue-as-canonical-input>`
|
|
20
43
|
*
|
|
@@ -23,6 +46,22 @@ export type MemberAuthorizationUrnInput = Readonly<{
|
|
|
23
46
|
* - `urn:org:tax:acme-id`
|
|
24
47
|
*/
|
|
25
48
|
export declare function buildOrganizationAuthorizationUrn(input: OrganizationAuthorizationUrnInput): string;
|
|
49
|
+
/**
|
|
50
|
+
* Builds the globally scoped, host-independent legal organization identifier.
|
|
51
|
+
*
|
|
52
|
+
* Format:
|
|
53
|
+
* `urn:cds-<jurisdiction>:<version>:organization:<identifier-type>:<identifier-value>`
|
|
54
|
+
*
|
|
55
|
+
* Jurisdiction is mandatory because identifier schemes such as BN, EIN and
|
|
56
|
+
* TAX are interpreted by different national/subnational authorities.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildOrganizationAuthorizationUrnCds(input: OrganizationAuthorizationUrnCdsInput): string;
|
|
59
|
+
/**
|
|
60
|
+
* Builds a stable organization-member authorization identifier without a host.
|
|
61
|
+
* `roleType` is mandatory input but only `roleValue` is serialized, matching
|
|
62
|
+
* the compact role convention used by member did:web paths.
|
|
63
|
+
*/
|
|
64
|
+
export declare function buildOrganizationMemberAuthorizationUrnCds(input: OrganizationMemberAuthorizationUrnCdsInput): string;
|
|
26
65
|
/**
|
|
27
66
|
* Accepts either the legacy `TYPE|VALUE` input or an already-canonical
|
|
28
67
|
* `urn:org:...` identifier and always returns the canonical URN form.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
2
|
import { ClaimsOrganizationSchemaorg } from '../constants/schemaorg.js';
|
|
3
3
|
const ORGANIZATION_URN_PREFIX = 'urn:org:';
|
|
4
|
+
const CDS_ORGANIZATION_URN_PATTERN = /^urn:cds-([a-z]{2}(?:-[a-z0-9]{2,3})?):(v[1-9][0-9]*):organization:([a-z][a-z0-9-]*):([^:]+)$/;
|
|
5
|
+
const BARE_MULTIBASE_PATTERN = /^z[1-9A-HJ-NP-Za-km-z]+$/;
|
|
4
6
|
function normalizeTypeSegment(identifierType) {
|
|
5
7
|
return String(identifierType || '').trim().toLowerCase();
|
|
6
8
|
}
|
|
@@ -11,6 +13,10 @@ function normalizeValueSegment(identifierValue) {
|
|
|
11
13
|
* Canonical organization authorization identifier used by ledger, consent-like
|
|
12
14
|
* rules, and inter-tenant authorization persistence.
|
|
13
15
|
*
|
|
16
|
+
* @deprecated For new cross-jurisdiction identities use
|
|
17
|
+
* `buildOrganizationAuthorizationUrnCds`, which requires jurisdiction and
|
|
18
|
+
* grammar version. This legacy form remains readable during migration.
|
|
19
|
+
*
|
|
14
20
|
* Format:
|
|
15
21
|
* - `urn:org:<identifierType-lowercase>:<identifierValue-as-canonical-input>`
|
|
16
22
|
*
|
|
@@ -26,6 +32,52 @@ export function buildOrganizationAuthorizationUrn(input) {
|
|
|
26
32
|
}
|
|
27
33
|
return `${ORGANIZATION_URN_PREFIX}${identifierType}:${identifierValue}`;
|
|
28
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Builds the globally scoped, host-independent legal organization identifier.
|
|
37
|
+
*
|
|
38
|
+
* Format:
|
|
39
|
+
* `urn:cds-<jurisdiction>:<version>:organization:<identifier-type>:<identifier-value>`
|
|
40
|
+
*
|
|
41
|
+
* Jurisdiction is mandatory because identifier schemes such as BN, EIN and
|
|
42
|
+
* TAX are interpreted by different national/subnational authorities.
|
|
43
|
+
*/
|
|
44
|
+
export function buildOrganizationAuthorizationUrnCds(input) {
|
|
45
|
+
const jurisdiction = String(input.jurisdiction || '').trim().toLowerCase();
|
|
46
|
+
const version = String(input.version || 'v1').trim().toLowerCase();
|
|
47
|
+
const identifierType = normalizeTypeSegment(input.identifierType);
|
|
48
|
+
const identifierValue = normalizeValueSegment(input.identifierValue);
|
|
49
|
+
if (!/^[a-z]{2}(?:-[a-z0-9]{2,3})?$/.test(jurisdiction)) {
|
|
50
|
+
throw new Error('CDS organization authorization URN requires an ISO jurisdiction such as ES, US or CA-BC.');
|
|
51
|
+
}
|
|
52
|
+
if (!/^v[1-9][0-9]*$/.test(version)) {
|
|
53
|
+
throw new Error('CDS organization authorization URN requires a version such as v1.');
|
|
54
|
+
}
|
|
55
|
+
if (!/^[a-z][a-z0-9-]*$/.test(identifierType) || !identifierValue || identifierValue.includes(':')) {
|
|
56
|
+
throw new Error('CDS organization authorization URN requires a legal identifier type and colon-free value.');
|
|
57
|
+
}
|
|
58
|
+
return `urn:cds-${jurisdiction}:${version}:organization:${identifierType}:${identifierValue}`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Builds a stable organization-member authorization identifier without a host.
|
|
62
|
+
* `roleType` is mandatory input but only `roleValue` is serialized, matching
|
|
63
|
+
* the compact role convention used by member did:web paths.
|
|
64
|
+
*/
|
|
65
|
+
export function buildOrganizationMemberAuthorizationUrnCds(input) {
|
|
66
|
+
const organizationUrn = String(input.organizationUrn || '').trim();
|
|
67
|
+
const memberId = String(input.memberId || '').trim();
|
|
68
|
+
const roleType = String(input.roleType || '').trim();
|
|
69
|
+
const roleValue = String(input.roleValue || '').trim();
|
|
70
|
+
if (!CDS_ORGANIZATION_URN_PATTERN.test(organizationUrn)) {
|
|
71
|
+
throw new Error('CDS organization member authorization URN requires a canonical CDS organization URN.');
|
|
72
|
+
}
|
|
73
|
+
if (!BARE_MULTIBASE_PATTERN.test(memberId)) {
|
|
74
|
+
throw new Error('CDS organization member authorization URN requires memberId as bare base58btc multibase.');
|
|
75
|
+
}
|
|
76
|
+
if (!roleType || !roleValue || roleValue.includes(':')) {
|
|
77
|
+
throw new Error('CDS organization member authorization URN requires roleType and a colon-free roleValue.');
|
|
78
|
+
}
|
|
79
|
+
return `${organizationUrn}:member:${memberId}:${roleValue}`;
|
|
80
|
+
}
|
|
29
81
|
/**
|
|
30
82
|
* Accepts either the legacy `TYPE|VALUE` input or an already-canonical
|
|
31
83
|
* `urn:org:...` identifier and always returns the canonical URN form.
|