gdc-common-utils-ts 2.9.25 → 2.9.27
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 +12 -0
- package/dist/utils/clinical-resource-view.js +12 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/legal-sector-organization-urn.d.ts +33 -0
- package/dist/utils/legal-sector-organization-urn.js +78 -0
- package/dist/utils/organization-lifecycle.d.ts +4 -0
- package/dist/utils/organization-lifecycle.js +14 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -759,6 +759,18 @@ Example:
|
|
|
759
759
|
import { ICryptography, MlkemPublicJwk } from 'gdc-common-utils-ts/interfaces/Cryptography.types';
|
|
760
760
|
```
|
|
761
761
|
|
|
762
|
+
## Sector tenant identity
|
|
763
|
+
|
|
764
|
+
Use `buildLegalSectorOrganizationUrn(...)` to derive the stable public identity
|
|
765
|
+
of one legal organization inside one data-space sector. Use
|
|
766
|
+
`buildLegalSectorOrganizationAssetId(...)` for its opaque Fabric lookup key.
|
|
767
|
+
Changing the hosting provider or tenant DID does not change that key.
|
|
768
|
+
|
|
769
|
+
For tenant lifecycle calls, keep the neutral legal identifier in
|
|
770
|
+
`setIdentifierValue(...)` and select the exact hosted tenant with
|
|
771
|
+
`setTenantDid(...)`. Obtain that DID from the registered tenant/profile or
|
|
772
|
+
authority response; do not reconstruct it from display names.
|
|
773
|
+
|
|
762
774
|
## Auth-Flow Boundaries
|
|
763
775
|
|
|
764
776
|
This package provides primitives, not orchestration.
|
|
@@ -576,8 +576,19 @@ function resolveDate(resourceType, claims, resource) {
|
|
|
576
576
|
AllergyIntoleranceClaimsFhirApi.OnsetDateTime,
|
|
577
577
|
]) || resolveFhirDate(resourceType, resource);
|
|
578
578
|
}
|
|
579
|
+
const resourceDate = readCanonicalClaimValue(claims, `${resourceType}.date`);
|
|
580
|
+
if (resourceDate)
|
|
581
|
+
return resourceDate;
|
|
582
|
+
if (resourceType === ResourceTypesFhirR4.Observation) {
|
|
583
|
+
const effectiveDateTime = readCanonicalClaimValue(claims, ObservationClaim.EffectiveDateTime);
|
|
584
|
+
if (effectiveDateTime)
|
|
585
|
+
return effectiveDateTime;
|
|
586
|
+
}
|
|
587
|
+
const nativeDate = resolveFhirDate(resourceType, resource);
|
|
588
|
+
if (nativeDate)
|
|
589
|
+
return nativeDate;
|
|
579
590
|
const genericDate = findBySuffix(claims, '.date');
|
|
580
|
-
return genericDate ||
|
|
591
|
+
return genericDate || undefined;
|
|
581
592
|
}
|
|
582
593
|
function resolvePeriodStart(resourceType, claims, resource) {
|
|
583
594
|
if (resourceType === ResourceTypesFhirR4.Consent) {
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -69,6 +69,7 @@ export * from './individual-smart.js';
|
|
|
69
69
|
export * from './individual-organization-claims.js';
|
|
70
70
|
export * from './inter-tenant-access-contract.js';
|
|
71
71
|
export * from './legal-organization-onboarding-editor.js';
|
|
72
|
+
export * from './legal-sector-organization-urn.js';
|
|
72
73
|
export * from './organization-lifecycle.js';
|
|
73
74
|
export * from './organization-employee-lifecycle.js';
|
|
74
75
|
export * from './organization-did-binding.js';
|
package/dist/utils/index.js
CHANGED
|
@@ -69,6 +69,7 @@ export * from './individual-smart.js';
|
|
|
69
69
|
export * from './individual-organization-claims.js';
|
|
70
70
|
export * from './inter-tenant-access-contract.js';
|
|
71
71
|
export * from './legal-organization-onboarding-editor.js';
|
|
72
|
+
export * from './legal-sector-organization-urn.js';
|
|
72
73
|
export * from './organization-lifecycle.js';
|
|
73
74
|
export * from './organization-employee-lifecycle.js';
|
|
74
75
|
export * from './organization-did-binding.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type LegalSectorOrganizationUrnInput = Readonly<{
|
|
2
|
+
/** Governed data-space sector, for example `health-care` or `animal-care`. */
|
|
3
|
+
sector: string;
|
|
4
|
+
/** ISO 3166-1 alpha-2 country code. */
|
|
5
|
+
country: string;
|
|
6
|
+
/** Optional ISO 3166-2 or governed first administrative subdivision. */
|
|
7
|
+
administrativeSublevel1?: string;
|
|
8
|
+
/** Optional second administrative subdivision. */
|
|
9
|
+
administrativeSublevel2?: string;
|
|
10
|
+
/** Optional third administrative subdivision. */
|
|
11
|
+
administrativeSublevel3?: string;
|
|
12
|
+
/** HL7 v2-0203-derived or governed extension code, such as TAX, EN or BN. */
|
|
13
|
+
identifierType: string;
|
|
14
|
+
/** Issuer-defined official identifier. Its casing is preserved. */
|
|
15
|
+
identifierValue: string;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Builds the organization-level public identifier from the governed legal URN
|
|
19
|
+
* grammar. All structural tokens are lower-cased; the official identifier is
|
|
20
|
+
* preserved exactly after trimming and NFKC normalization.
|
|
21
|
+
*
|
|
22
|
+
* Format:
|
|
23
|
+
* `urn:legal:<sector>:<country>:<admin1>:<admin2>:<admin3>:organization:<type>:<official-id>`
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildLegalSectorOrganizationUrn(input: LegalSectorOrganizationUrnInput): string;
|
|
26
|
+
/**
|
|
27
|
+
* Builds the stable Fabric lookup key for one organization-sector binding.
|
|
28
|
+
*
|
|
29
|
+
* The exact canonical legal-sector URN UTF-8 bytes are encoded as
|
|
30
|
+
* `urn:multibase:base58btc(multihash(SHA3-256))`. A provider/DID rotation does
|
|
31
|
+
* not change this key; only the binding asset state and history change.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildLegalSectorOrganizationAssetId(organizationUrn: string): string;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Copyright 2026 ConnectHealth.info (Connectin Solution & Applications Ltd.) under the Apache License, Version 2.0.
|
|
2
|
+
import { UrnPrefixes } from '../constants/urn.js';
|
|
3
|
+
import { encodeMultibaseSha3 } from './multibasehash.js';
|
|
4
|
+
const NORMALIZED_SEGMENT_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
5
|
+
function normalizeRequiredSegment(value, field) {
|
|
6
|
+
const normalized = String(value || '').trim().normalize('NFKC').toLowerCase();
|
|
7
|
+
if (!NORMALIZED_SEGMENT_PATTERN.test(normalized)) {
|
|
8
|
+
throw new Error(`Legal sector organization URN requires a valid ${field}.`);
|
|
9
|
+
}
|
|
10
|
+
return normalized;
|
|
11
|
+
}
|
|
12
|
+
function normalizeOptionalSegment(value, field) {
|
|
13
|
+
if (!String(value || '').trim())
|
|
14
|
+
return '';
|
|
15
|
+
return normalizeRequiredSegment(String(value), field);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Builds the organization-level public identifier from the governed legal URN
|
|
19
|
+
* grammar. All structural tokens are lower-cased; the official identifier is
|
|
20
|
+
* preserved exactly after trimming and NFKC normalization.
|
|
21
|
+
*
|
|
22
|
+
* Format:
|
|
23
|
+
* `urn:legal:<sector>:<country>:<admin1>:<admin2>:<admin3>:organization:<type>:<official-id>`
|
|
24
|
+
*/
|
|
25
|
+
export function buildLegalSectorOrganizationUrn(input) {
|
|
26
|
+
const sector = normalizeRequiredSegment(input.sector, 'sector');
|
|
27
|
+
const country = normalizeRequiredSegment(input.country, 'country');
|
|
28
|
+
if (!/^[a-z]{2}$/.test(country)) {
|
|
29
|
+
throw new Error('Legal sector organization URN requires an ISO 3166-1 alpha-2 country.');
|
|
30
|
+
}
|
|
31
|
+
const administrativeSublevel1 = normalizeOptionalSegment(input.administrativeSublevel1, 'administrativeSublevel1');
|
|
32
|
+
const administrativeSublevel2 = normalizeOptionalSegment(input.administrativeSublevel2, 'administrativeSublevel2');
|
|
33
|
+
const administrativeSublevel3 = normalizeOptionalSegment(input.administrativeSublevel3, 'administrativeSublevel3');
|
|
34
|
+
const identifierType = normalizeRequiredSegment(input.identifierType, 'identifierType');
|
|
35
|
+
const identifierValue = String(input.identifierValue || '').trim().normalize('NFKC');
|
|
36
|
+
if (!identifierValue || /[:\s]/u.test(identifierValue)) {
|
|
37
|
+
throw new Error('Legal sector organization URN requires a colon-free identifierValue.');
|
|
38
|
+
}
|
|
39
|
+
return [
|
|
40
|
+
'urn',
|
|
41
|
+
'legal',
|
|
42
|
+
sector,
|
|
43
|
+
country,
|
|
44
|
+
administrativeSublevel1,
|
|
45
|
+
administrativeSublevel2,
|
|
46
|
+
administrativeSublevel3,
|
|
47
|
+
'organization',
|
|
48
|
+
identifierType,
|
|
49
|
+
identifierValue,
|
|
50
|
+
].join(':');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Builds the stable Fabric lookup key for one organization-sector binding.
|
|
54
|
+
*
|
|
55
|
+
* The exact canonical legal-sector URN UTF-8 bytes are encoded as
|
|
56
|
+
* `urn:multibase:base58btc(multihash(SHA3-256))`. A provider/DID rotation does
|
|
57
|
+
* not change this key; only the binding asset state and history change.
|
|
58
|
+
*/
|
|
59
|
+
export function buildLegalSectorOrganizationAssetId(organizationUrn) {
|
|
60
|
+
const canonicalUrn = String(organizationUrn || '').trim().normalize('NFKC');
|
|
61
|
+
const parts = canonicalUrn.split(':');
|
|
62
|
+
if (parts.length !== 10 || parts[0] !== 'urn' || parts[1] !== 'legal' || parts[7] !== 'organization') {
|
|
63
|
+
throw new Error('Legal sector organization asset ID requires a canonical organization-level urn:legal identifier.');
|
|
64
|
+
}
|
|
65
|
+
const rebuilt = buildLegalSectorOrganizationUrn({
|
|
66
|
+
sector: parts[2],
|
|
67
|
+
country: parts[3],
|
|
68
|
+
administrativeSublevel1: parts[4],
|
|
69
|
+
administrativeSublevel2: parts[5],
|
|
70
|
+
administrativeSublevel3: parts[6],
|
|
71
|
+
identifierType: parts[8],
|
|
72
|
+
identifierValue: parts[9],
|
|
73
|
+
});
|
|
74
|
+
if (rebuilt !== canonicalUrn) {
|
|
75
|
+
throw new Error('Legal sector organization asset ID requires an already-canonical urn:legal identifier.');
|
|
76
|
+
}
|
|
77
|
+
return `${UrnPrefixes.Multibase}${encodeMultibaseSha3(canonicalUrn, 256)}`;
|
|
78
|
+
}
|
|
@@ -39,6 +39,8 @@ export declare class OrganizationLifecycleEditor {
|
|
|
39
39
|
setIdentifierValue(identifierValue: string): this;
|
|
40
40
|
/** Sets the jurisdiction-defined identifier type independently from its complete value. */
|
|
41
41
|
setIdentifierType(identifierType: string): this;
|
|
42
|
+
/** Selects the exact hosted tenant DID when one legal organization has multiple sector tenants. */
|
|
43
|
+
setTenantDid(tenantDid: string): this;
|
|
42
44
|
setTaxId(taxId: string): this;
|
|
43
45
|
setOperation(operation: OrganizationLifecycleOperation): this;
|
|
44
46
|
setRequestType(requestType: string): this;
|
|
@@ -48,6 +50,8 @@ export declare class OrganizationLifecycleEditor {
|
|
|
48
50
|
getIdentifierValue(): string | undefined;
|
|
49
51
|
/** Returns the jurisdiction-defined identifier type used by lifecycle authorization. */
|
|
50
52
|
getIdentifierType(): string | undefined;
|
|
53
|
+
/** Returns the exact hosted tenant DID selected for this lifecycle operation. */
|
|
54
|
+
getTenantDid(): string | undefined;
|
|
51
55
|
getTaxId(): string | undefined;
|
|
52
56
|
getOperation(): OrganizationLifecycleOperation;
|
|
53
57
|
getRequestType(): string | undefined;
|
|
@@ -83,6 +83,16 @@ export class OrganizationLifecycleEditor {
|
|
|
83
83
|
});
|
|
84
84
|
return this;
|
|
85
85
|
}
|
|
86
|
+
/** Selects the exact hosted tenant DID when one legal organization has multiple sector tenants. */
|
|
87
|
+
setTenantDid(tenantDid) {
|
|
88
|
+
this.draft = patchDraft(this.draft, {
|
|
89
|
+
claims: {
|
|
90
|
+
...this.draft.claims,
|
|
91
|
+
[ClaimsOrganizationSchemaorg.sameAs]: String(tenantDid).trim(),
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
86
96
|
setTaxId(taxId) {
|
|
87
97
|
this.draft = patchDraft(this.draft, {
|
|
88
98
|
claims: {
|
|
@@ -117,6 +127,10 @@ export class OrganizationLifecycleEditor {
|
|
|
117
127
|
getIdentifierType() {
|
|
118
128
|
return normalizeText(this.draft.claims[ClaimsOrganizationSchemaorg.identifierType]) || undefined;
|
|
119
129
|
}
|
|
130
|
+
/** Returns the exact hosted tenant DID selected for this lifecycle operation. */
|
|
131
|
+
getTenantDid() {
|
|
132
|
+
return normalizeText(this.draft.claims[ClaimsOrganizationSchemaorg.sameAs]) || undefined;
|
|
133
|
+
}
|
|
120
134
|
getTaxId() {
|
|
121
135
|
return normalizeText(this.draft.claims[ClaimsOrganizationSchemaorg.taxId]) || undefined;
|
|
122
136
|
}
|