gdc-common-utils-ts 2.6.0 → 2.6.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/dist/constants/schemaorg.d.ts +4 -0
- package/dist/constants/schemaorg.js +4 -0
- package/dist/examples/break-glass.d.ts +10 -0
- package/dist/examples/break-glass.js +11 -0
- package/dist/examples/index.d.ts +1 -0
- package/dist/examples/index.js +1 -0
- package/dist/examples/shared.d.ts +2 -0
- package/dist/examples/shared.js +2 -0
- package/dist/utils/break-glass-policy.d.ts +50 -0
- package/dist/utils/break-glass-policy.js +85 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/subject-identity-binding.js +1 -1
- package/package.json +1 -1
|
@@ -159,6 +159,10 @@ export declare enum ClaimsOrderSchemaorg {
|
|
|
159
159
|
export declare enum ClaimsPersonSchemaorg {
|
|
160
160
|
/** Schema.org additional type; service-controller VCs use bare `RESPRSN`. */
|
|
161
161
|
additionalType = "org.schema.Person.additionalType",
|
|
162
|
+
/** ISO 3166 country governing one independently evidenced identity document. */
|
|
163
|
+
addressCountry = "org.schema.Person.address.addressCountry",
|
|
164
|
+
/** ISO 3166-2 subdivision when the evidenced identifier is regional. */
|
|
165
|
+
addressRegion = "org.schema.Person.address.addressRegion",
|
|
162
166
|
/** Second surname or mother's maiden name */
|
|
163
167
|
additionalName = "org.schema.Person.additionalName",
|
|
164
168
|
/** Short friendly name */
|
|
@@ -167,6 +167,10 @@ export var ClaimsPersonSchemaorg;
|
|
|
167
167
|
(function (ClaimsPersonSchemaorg) {
|
|
168
168
|
/** Schema.org additional type; service-controller VCs use bare `RESPRSN`. */
|
|
169
169
|
ClaimsPersonSchemaorg["additionalType"] = "org.schema.Person.additionalType";
|
|
170
|
+
/** ISO 3166 country governing one independently evidenced identity document. */
|
|
171
|
+
ClaimsPersonSchemaorg["addressCountry"] = "org.schema.Person.address.addressCountry";
|
|
172
|
+
/** ISO 3166-2 subdivision when the evidenced identifier is regional. */
|
|
173
|
+
ClaimsPersonSchemaorg["addressRegion"] = "org.schema.Person.address.addressRegion";
|
|
170
174
|
/** Second surname or mother's maiden name */
|
|
171
175
|
ClaimsPersonSchemaorg["additionalName"] = "org.schema.Person.additionalName";
|
|
172
176
|
/** Short friendly name */
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Stable product-neutral incident identifier for emergency-access examples. */
|
|
2
|
+
export declare const EXAMPLE_BREAK_GLASS_INCIDENT_ID: "incident-example-001";
|
|
3
|
+
/** Stable product-neutral justification for emergency-access examples. */
|
|
4
|
+
export declare const EXAMPLE_BREAK_GLASS_JUSTIFICATION: "Immediate governed emergency access is required.";
|
|
5
|
+
/** Stable clock instant for deterministic emergency-access examples. */
|
|
6
|
+
export declare const EXAMPLE_BREAK_GLASS_ISSUED_AT: "2026-08-31T12:00:00.000Z";
|
|
7
|
+
/** Stable notifier result for deterministic emergency-access examples. */
|
|
8
|
+
export declare const EXAMPLE_BREAK_GLASS_NOTIFICATION_ID: "notification-example-001";
|
|
9
|
+
/** Stable product-neutral ledger channel for emergency-access examples. */
|
|
10
|
+
export declare const EXAMPLE_BREAK_GLASS_LEDGER_DATA_CHANNEL: "clinical-evidence-eu";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
/** Stable product-neutral incident identifier for emergency-access examples. */
|
|
3
|
+
export const EXAMPLE_BREAK_GLASS_INCIDENT_ID = 'incident-example-001';
|
|
4
|
+
/** Stable product-neutral justification for emergency-access examples. */
|
|
5
|
+
export const EXAMPLE_BREAK_GLASS_JUSTIFICATION = 'Immediate governed emergency access is required.';
|
|
6
|
+
/** Stable clock instant for deterministic emergency-access examples. */
|
|
7
|
+
export const EXAMPLE_BREAK_GLASS_ISSUED_AT = '2026-08-31T12:00:00.000Z';
|
|
8
|
+
/** Stable notifier result for deterministic emergency-access examples. */
|
|
9
|
+
export const EXAMPLE_BREAK_GLASS_NOTIFICATION_ID = 'notification-example-001';
|
|
10
|
+
/** Stable product-neutral ledger channel for emergency-access examples. */
|
|
11
|
+
export const EXAMPLE_BREAK_GLASS_LEDGER_DATA_CHANNEL = 'clinical-evidence-eu';
|
package/dist/examples/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export * from './license';
|
|
|
12
12
|
export * from './organization-role-license';
|
|
13
13
|
export * from './invoice';
|
|
14
14
|
export * from './inter-tenant-access-contract';
|
|
15
|
+
export * from './break-glass';
|
|
15
16
|
export * from './subject-identity-binding';
|
|
16
17
|
export * from './related-person';
|
|
17
18
|
export * from './consent-access';
|
package/dist/examples/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export * from './license.js';
|
|
|
12
12
|
export * from './organization-role-license.js';
|
|
13
13
|
export * from './invoice.js';
|
|
14
14
|
export * from './inter-tenant-access-contract.js';
|
|
15
|
+
export * from './break-glass.js';
|
|
15
16
|
export * from './subject-identity-binding.js';
|
|
16
17
|
export * from './related-person.js';
|
|
17
18
|
export * from './consent-access.js';
|
|
@@ -141,6 +141,8 @@ export declare const EXAMPLE_PROVIDER_ORGANIZATION_DID: "did:web:hospital.acme.o
|
|
|
141
141
|
export declare const EXAMPLE_PROVIDER_ORGANIZATION_URL: "https://hospital.acme.org";
|
|
142
142
|
export declare const EXAMPLE_GATEWAY_PUBLIC_ORIGIN: "https://gateway.example.com";
|
|
143
143
|
export declare const EXAMPLE_HOST_PUBLIC_HOSTNAME: "host.example.com";
|
|
144
|
+
export declare const EXAMPLE_PERSON_CARD_URI: "urn:example:card:person:subject-001";
|
|
145
|
+
export declare const EXAMPLE_ANIMAL_CARD_URI: "urn:example:card:animal:subject-002";
|
|
144
146
|
export declare const EXAMPLE_PROVIDER_TAX_ID: "VATES-B00112233";
|
|
145
147
|
export declare const EXAMPLE_PROVIDER_DOMAIN: "health-care.provider.example.org";
|
|
146
148
|
/** Synthetic public portal domain used to derive organization path DIDs. */
|
package/dist/examples/shared.js
CHANGED
|
@@ -159,6 +159,8 @@ export const EXAMPLE_PROVIDER_ORGANIZATION_DID = 'did:web:hospital.acme.org';
|
|
|
159
159
|
export const EXAMPLE_PROVIDER_ORGANIZATION_URL = 'https://hospital.acme.org';
|
|
160
160
|
export const EXAMPLE_GATEWAY_PUBLIC_ORIGIN = 'https://gateway.example.com';
|
|
161
161
|
export const EXAMPLE_HOST_PUBLIC_HOSTNAME = 'host.example.com';
|
|
162
|
+
export const EXAMPLE_PERSON_CARD_URI = 'urn:example:card:person:subject-001';
|
|
163
|
+
export const EXAMPLE_ANIMAL_CARD_URI = 'urn:example:card:animal:subject-002';
|
|
162
164
|
export const EXAMPLE_PROVIDER_TAX_ID = 'VATES-B00112233';
|
|
163
165
|
export const EXAMPLE_PROVIDER_DOMAIN = 'health-care.provider.example.org';
|
|
164
166
|
/** Synthetic public portal domain used to derive organization path DIDs. */
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Subject categories accepted by the shared emergency-access contract. */
|
|
2
|
+
export declare const BreakGlassSubjectKinds: Readonly<{
|
|
3
|
+
readonly Human: "human";
|
|
4
|
+
readonly Animal: "animal";
|
|
5
|
+
}>;
|
|
6
|
+
export type BreakGlassSubjectKind = typeof BreakGlassSubjectKinds[keyof typeof BreakGlassSubjectKinds];
|
|
7
|
+
/** Governed reasons accepted by the shared emergency-access contract. */
|
|
8
|
+
export declare const BreakGlassReasonCodes: Readonly<{
|
|
9
|
+
readonly LifeThreatening: "life-threatening";
|
|
10
|
+
readonly SeriousImminentHarm: "serious-imminent-harm";
|
|
11
|
+
readonly UnconsciousOrIncapacitated: "unconscious-or-incapacitated";
|
|
12
|
+
readonly AnimalEmergency: "animal-emergency";
|
|
13
|
+
}>;
|
|
14
|
+
export type BreakGlassReasonCode = typeof BreakGlassReasonCodes[keyof typeof BreakGlassReasonCodes];
|
|
15
|
+
/** Transport-neutral emergency request data evaluated before persistence. */
|
|
16
|
+
export type BreakGlassRequest = Readonly<{
|
|
17
|
+
incidentId: string;
|
|
18
|
+
subjectKind: BreakGlassSubjectKind;
|
|
19
|
+
reasonCode: BreakGlassReasonCode;
|
|
20
|
+
justification: string;
|
|
21
|
+
}>;
|
|
22
|
+
export type BreakGlassPolicyInput = Readonly<{
|
|
23
|
+
routeSector: string;
|
|
24
|
+
subjectKind: BreakGlassSubjectKind;
|
|
25
|
+
professionalRole: string;
|
|
26
|
+
requestedScope: string;
|
|
27
|
+
reasonCode: BreakGlassReasonCode;
|
|
28
|
+
}>;
|
|
29
|
+
export type BreakGlassPolicyDecision = Readonly<{
|
|
30
|
+
allowed: true;
|
|
31
|
+
maxLifetimeSeconds: 900;
|
|
32
|
+
}> | Readonly<{
|
|
33
|
+
allowed: false;
|
|
34
|
+
reason: string;
|
|
35
|
+
}>;
|
|
36
|
+
/** Extension hook owned by a domain package that recognizes its exact subject identifier. */
|
|
37
|
+
export type BreakGlassSubjectKindMatcher = (subjectDid: string, subjectKind: BreakGlassSubjectKind) => boolean;
|
|
38
|
+
/** Matches only neutral subject identifiers. Product/card formats require an injected matcher. */
|
|
39
|
+
export declare const matchesCanonicalBreakGlassSubjectKind: BreakGlassSubjectKindMatcher;
|
|
40
|
+
/**
|
|
41
|
+
* Resolves one subject identifier through the neutral matcher plus optional
|
|
42
|
+
* domain matchers. Shared code never imports a product-specific card parser.
|
|
43
|
+
*/
|
|
44
|
+
export declare function matchesBreakGlassSubjectKind(subjectDid: string, subjectKind: BreakGlassSubjectKind, domainMatchers?: readonly BreakGlassSubjectKindMatcher[]): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Evaluates the reusable sector, role, reason and read-only scope policy.
|
|
47
|
+
* GW services orchestrate persistence and audit; domain packages may extend
|
|
48
|
+
* identifier recognition through the shared matcher interface.
|
|
49
|
+
*/
|
|
50
|
+
export declare function evaluateBreakGlassPolicy(input: BreakGlassPolicyInput): BreakGlassPolicyDecision;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
import { HealthcareActorRoleCodes } from '../constants/healthcare.js';
|
|
3
|
+
import { DataspaceSectors } from '../constants/sectors.js';
|
|
4
|
+
/** Subject categories accepted by the shared emergency-access contract. */
|
|
5
|
+
export const BreakGlassSubjectKinds = Object.freeze({
|
|
6
|
+
Human: 'human',
|
|
7
|
+
Animal: 'animal',
|
|
8
|
+
});
|
|
9
|
+
/** Governed reasons accepted by the shared emergency-access contract. */
|
|
10
|
+
export const BreakGlassReasonCodes = Object.freeze({
|
|
11
|
+
LifeThreatening: 'life-threatening',
|
|
12
|
+
SeriousImminentHarm: 'serious-imminent-harm',
|
|
13
|
+
UnconsciousOrIncapacitated: 'unconscious-or-incapacitated',
|
|
14
|
+
AnimalEmergency: 'animal-emergency',
|
|
15
|
+
});
|
|
16
|
+
const RESEARCH_SECTORS = new Set([
|
|
17
|
+
DataspaceSectors.HealthResearch,
|
|
18
|
+
DataspaceSectors.AnimalResearch,
|
|
19
|
+
DataspaceSectors.OneHealthResearch,
|
|
20
|
+
]);
|
|
21
|
+
function occupationCode(value) {
|
|
22
|
+
const normalized = String(value || '').trim();
|
|
23
|
+
return (normalized.includes('|') ? normalized.slice(normalized.lastIndexOf('|') + 1) : normalized)
|
|
24
|
+
.replace(/[^0-9]/g, '');
|
|
25
|
+
}
|
|
26
|
+
function isReadOnlyScope(scope) {
|
|
27
|
+
const tokens = String(scope || '').split(/\s+/).filter(Boolean);
|
|
28
|
+
return tokens.length > 0 && tokens.every((token) => {
|
|
29
|
+
const head = token.split('?', 1)[0] || '';
|
|
30
|
+
const permission = head.slice(head.lastIndexOf('.') + 1).toLowerCase();
|
|
31
|
+
return permission === 'r' || permission === 'rs';
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/** Matches only neutral subject identifiers. Product/card formats require an injected matcher. */
|
|
35
|
+
export const matchesCanonicalBreakGlassSubjectKind = (subjectDid, subjectKind) => {
|
|
36
|
+
const normalized = String(subjectDid || '').trim().toLowerCase();
|
|
37
|
+
return subjectKind === BreakGlassSubjectKinds.Human
|
|
38
|
+
? normalized.includes(':individual:')
|
|
39
|
+
: normalized.includes(':animal:');
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Resolves one subject identifier through the neutral matcher plus optional
|
|
43
|
+
* domain matchers. Shared code never imports a product-specific card parser.
|
|
44
|
+
*/
|
|
45
|
+
export function matchesBreakGlassSubjectKind(subjectDid, subjectKind, domainMatchers = []) {
|
|
46
|
+
return [matchesCanonicalBreakGlassSubjectKind, ...domainMatchers]
|
|
47
|
+
.some((matcher) => matcher(subjectDid, subjectKind));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Evaluates the reusable sector, role, reason and read-only scope policy.
|
|
51
|
+
* GW services orchestrate persistence and audit; domain packages may extend
|
|
52
|
+
* identifier recognition through the shared matcher interface.
|
|
53
|
+
*/
|
|
54
|
+
export function evaluateBreakGlassPolicy(input) {
|
|
55
|
+
const routeSector = String(input.routeSector || '').trim().toLowerCase();
|
|
56
|
+
if (RESEARCH_SECTORS.has(routeSector)) {
|
|
57
|
+
return { allowed: false, reason: 'research_sector_forbidden' };
|
|
58
|
+
}
|
|
59
|
+
if (!isReadOnlyScope(input.requestedScope)) {
|
|
60
|
+
return { allowed: false, reason: 'read_only_scope_required' };
|
|
61
|
+
}
|
|
62
|
+
const role = occupationCode(input.professionalRole);
|
|
63
|
+
if (input.subjectKind === BreakGlassSubjectKinds.Human) {
|
|
64
|
+
if (routeSector !== DataspaceSectors.HealthCare) {
|
|
65
|
+
return { allowed: false, reason: 'human_sector_mismatch' };
|
|
66
|
+
}
|
|
67
|
+
if (!role.startsWith(HealthcareActorRoleCodes.MedicalDoctors)) {
|
|
68
|
+
return { allowed: false, reason: 'professional_role_not_authorized' };
|
|
69
|
+
}
|
|
70
|
+
if (input.reasonCode === BreakGlassReasonCodes.AnimalEmergency) {
|
|
71
|
+
return { allowed: false, reason: 'reason_subject_mismatch' };
|
|
72
|
+
}
|
|
73
|
+
return { allowed: true, maxLifetimeSeconds: 900 };
|
|
74
|
+
}
|
|
75
|
+
if (routeSector !== DataspaceSectors.AnimalCare) {
|
|
76
|
+
return { allowed: false, reason: 'animal_sector_mismatch' };
|
|
77
|
+
}
|
|
78
|
+
if (role !== HealthcareActorRoleCodes.Veterinarian) {
|
|
79
|
+
return { allowed: false, reason: 'professional_role_not_authorized' };
|
|
80
|
+
}
|
|
81
|
+
if (input.reasonCode !== BreakGlassReasonCodes.AnimalEmergency) {
|
|
82
|
+
return { allowed: false, reason: 'reason_subject_mismatch' };
|
|
83
|
+
}
|
|
84
|
+
return { allowed: true, maxLifetimeSeconds: 900 };
|
|
85
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -11,7 +11,7 @@ function isDidWeb(value) {
|
|
|
11
11
|
}
|
|
12
12
|
function isPhysicalSupportDid(value) {
|
|
13
13
|
const segments = value.toLowerCase().split(':');
|
|
14
|
-
return segments.includes('card')
|
|
14
|
+
return segments.includes('card');
|
|
15
15
|
}
|
|
16
16
|
function isIndividualIdentityDid(value) {
|
|
17
17
|
return isDidWeb(value) && !isPhysicalSupportDid(value);
|