vet-data-utils-ts 0.5.23 → 0.5.25
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 +29 -1
- package/dist/health-dcat.d.ts +125 -0
- package/dist/health-dcat.js +240 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/sectors.d.ts +1 -0
- package/dist/sectors.js +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -172,7 +172,7 @@ It is not an implemented importer API.
|
|
|
172
172
|
|
|
173
173
|
`vet-data-utils-ts/sectors` extends the frozen CORE catalog without changing
|
|
174
174
|
`gdc-common-utils-ts` or GW CORE. It adds the product-neutral
|
|
175
|
-
`public-safety`
|
|
175
|
+
`public-health` and `public-safety` values used by explicitly governed portal allowlists. Sector
|
|
176
176
|
selection classifies an organization service; it does not grant membership,
|
|
177
177
|
an occupation, an index role or access to any person or animal.
|
|
178
178
|
|
|
@@ -182,11 +182,39 @@ import { ExtendedDataspaceSectors } from 'vet-data-utils-ts/sectors'
|
|
|
182
182
|
// The portal chooses its exact subset. Do not expose the complete catalog as
|
|
183
183
|
// an authorization decision or infer capabilities from this selection.
|
|
184
184
|
const allowedSectors = [
|
|
185
|
+
ExtendedDataspaceSectors.PublicHealth,
|
|
185
186
|
ExtendedDataspaceSectors.PublicSafety,
|
|
186
187
|
ExtendedDataspaceSectors.AnimalCare,
|
|
187
188
|
] as const
|
|
188
189
|
```
|
|
189
190
|
|
|
191
|
+
## Health dataset catalogues
|
|
192
|
+
|
|
193
|
+
`vet-data-utils-ts/health-dcat` provides product-local, claims-first
|
|
194
|
+
HealthDCAT metadata for public-health, care, insurance, lab and research
|
|
195
|
+
organizations. Only the organization's controller creates its marketplace
|
|
196
|
+
Catalogue or authorizes federation by a host service to another dataspace.
|
|
197
|
+
The tenant remains the publisher; the host does not become the data holder.
|
|
198
|
+
|
|
199
|
+
Dataset creation records the actual holder, access level, source and
|
|
200
|
+
provenance. Spreadsheet ingestion, an individual index, a digital-twin import,
|
|
201
|
+
and pseudonymization are data-processing facts, not publication authority.
|
|
202
|
+
An intermediary therefore needs an explicit authorization reference. A
|
|
203
|
+
non-public Dataset must reference its HDAB Distribution, and structured data
|
|
204
|
+
must reference its CSVW variable TableGroup. The flat keys are compact forms
|
|
205
|
+
of DCAT-AP, HealthDCAT-AP, DPV, PROV-O and GeoDCAT-AP properties; JSON-LD/DSP
|
|
206
|
+
serialization remains an explicit transport projection.
|
|
207
|
+
|
|
208
|
+
ICA advertisement exposes the host catalogue endpoint and supported discovery
|
|
209
|
+
profile, not a tenant list. Restricted catalogue discovery references OpenID
|
|
210
|
+
Federation 1.0 trust anchors and OpenID4VP 1.0 presentation definitions;
|
|
211
|
+
catalogue exchange remains Dataspace Protocol 2025-1. X.509/TLS chain
|
|
212
|
+
validation, federation trust resolution, VP verification and catalogue
|
|
213
|
+
authorization are separate checks performed by their owning adapters.
|
|
214
|
+
`normalizeHealthDcatResource()` rejects unknown or nested index claims, while
|
|
215
|
+
`projectHealthDcatResourceToJsonLd()` is the explicit standards serialization
|
|
216
|
+
boundary used by host/dataspace adapters.
|
|
217
|
+
|
|
190
218
|
`vet-data-utils-ts/organization-application` is the product-neutral application
|
|
191
219
|
value used by UHC UNID, VetChain and SOSChain adapters. The host supplies the
|
|
192
220
|
allowed sector list and translated labels. The value contains one official
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/** Product sectors whose organization UI may expose HealthDCAT publication. */
|
|
2
|
+
export declare const HealthDcatPublisherSectors: readonly ["public-health", "animal-care", "health-care", "animal-insurance", "health-insurance", "onehealth-insurance", "animal-lab", "health-lab", "onehealth-lab", "animal-research", "health-research", "onehealth-research"];
|
|
3
|
+
export type HealthDcatPublisherSector = typeof HealthDcatPublisherSectors[number];
|
|
4
|
+
export type HealthDcatAccessRights = 'PUBLIC' | 'RESTRICTED' | 'NON_PUBLIC';
|
|
5
|
+
export type HealthDcatSourceKind = 'individual-index' | 'spreadsheet-import' | 'digital-twin-import' | 'curated-dataset';
|
|
6
|
+
export type HealthDcatAnonymizationStatus = 'anonymous' | 'pseudonymized' | 'personal';
|
|
7
|
+
export type HealthDcatPublisherRole = 'data-holder' | 'authorized-intermediary';
|
|
8
|
+
type ClaimValue = string | boolean | number | readonly string[];
|
|
9
|
+
/** Standards-derived flat Dataset claims for indexed product persistence. */
|
|
10
|
+
export declare enum ClaimsHealthDcatDataset {
|
|
11
|
+
identifier = "dct.identifier",
|
|
12
|
+
title = "dct.title",
|
|
13
|
+
description = "dct.description",
|
|
14
|
+
creator = "dct.creator",
|
|
15
|
+
publisher = "dct.publisher",
|
|
16
|
+
custodian = "geodcatap.custodian",
|
|
17
|
+
accessRights = "dct.accessRights",
|
|
18
|
+
applicableLegislation = "dcatap.applicableLegislation",
|
|
19
|
+
distribution = "dcat.distribution",
|
|
20
|
+
source = "dct.source",
|
|
21
|
+
provenance = "dct.provenance",
|
|
22
|
+
personalData = "dpv.hasPersonalData",
|
|
23
|
+
healthCategory = "healthdcatap.healthCategory",
|
|
24
|
+
healthDataAccessBody = "healthdcatap.hdab",
|
|
25
|
+
structuredData = "healthdcatap.hasStructuredData",
|
|
26
|
+
variables = "healthdcatap.hasVariables",
|
|
27
|
+
authorizationReference = "prov.qualifiedAttribution",
|
|
28
|
+
userSelected = "Dataset.user-selected"
|
|
29
|
+
}
|
|
30
|
+
/** Standards-derived flat Catalogue claims for indexed product persistence. */
|
|
31
|
+
export declare enum ClaimsHealthDcatCatalog {
|
|
32
|
+
identifier = "dct.identifier",
|
|
33
|
+
title = "dct.title",
|
|
34
|
+
description = "dct.description",
|
|
35
|
+
creator = "dct.creator",
|
|
36
|
+
publisher = "dct.publisher",
|
|
37
|
+
applicableLegislation = "dcatap.applicableLegislation",
|
|
38
|
+
dataset = "dcat.dataset",
|
|
39
|
+
userSelected = "Catalog.user-selected"
|
|
40
|
+
}
|
|
41
|
+
/** Complete allowlist for product-persisted HealthDCAT flat claims. */
|
|
42
|
+
export declare const HealthDcatFlatClaimCatalog: Readonly<{
|
|
43
|
+
readonly Dataset: readonly ClaimsHealthDcatDataset[];
|
|
44
|
+
readonly Catalog: readonly ClaimsHealthDcatCatalog[];
|
|
45
|
+
}>;
|
|
46
|
+
export type HealthDcatResource = Readonly<{
|
|
47
|
+
resourceType: 'Dataset' | 'Catalog';
|
|
48
|
+
id: string;
|
|
49
|
+
meta: Readonly<{
|
|
50
|
+
claims: Readonly<Record<string, ClaimValue>>;
|
|
51
|
+
}>;
|
|
52
|
+
}>;
|
|
53
|
+
/** Validates an indexed HealthDCAT resource and rejects nested or unknown claims. */
|
|
54
|
+
export declare function normalizeHealthDcatResource(candidate: unknown): HealthDcatResource;
|
|
55
|
+
/** Explicitly projects internal flat claims to DCAT/HealthDCAT JSON-LD. */
|
|
56
|
+
export declare function projectHealthDcatResourceToJsonLd(resource: unknown): Readonly<Record<string, unknown>>;
|
|
57
|
+
type ControllerAuthority = Readonly<{
|
|
58
|
+
organizationIdentifier: string;
|
|
59
|
+
controllerIdentifier: string;
|
|
60
|
+
actingControllerIdentifier: string;
|
|
61
|
+
}>;
|
|
62
|
+
/** Builds a claims-first HealthDCAT Dataset description under explicit tenant authority. */
|
|
63
|
+
export declare function buildHealthDcatDataset(input: ControllerAuthority & Readonly<{
|
|
64
|
+
sector: HealthDcatPublisherSector;
|
|
65
|
+
id: string;
|
|
66
|
+
title: string;
|
|
67
|
+
description: string;
|
|
68
|
+
publisherRole: HealthDcatPublisherRole;
|
|
69
|
+
dataHolderIdentifier: string;
|
|
70
|
+
authorizationReference?: string;
|
|
71
|
+
sourceKind: HealthDcatSourceKind;
|
|
72
|
+
sourceReference: string;
|
|
73
|
+
accessRights: HealthDcatAccessRights;
|
|
74
|
+
anonymizationStatus: HealthDcatAnonymizationStatus;
|
|
75
|
+
healthCategory: readonly string[];
|
|
76
|
+
healthDataAccessBodyIdentifier: string;
|
|
77
|
+
structuredData: boolean;
|
|
78
|
+
variableTableGroupIdentifiers?: readonly string[];
|
|
79
|
+
distributionIdentifiers: readonly string[];
|
|
80
|
+
applicableLegislation: readonly string[];
|
|
81
|
+
}>): HealthDcatResource;
|
|
82
|
+
/** Builds the tenant-owned marketplace Catalogue; creation is controller-only. */
|
|
83
|
+
export declare function buildHealthDcatCatalog(input: ControllerAuthority & Readonly<{
|
|
84
|
+
id: string;
|
|
85
|
+
title: string;
|
|
86
|
+
description: string;
|
|
87
|
+
datasetIdentifiers: readonly string[];
|
|
88
|
+
applicableLegislation: readonly string[];
|
|
89
|
+
}>): HealthDcatResource;
|
|
90
|
+
/** Creates an auditable tenant authorization for host-provided catalogue federation. */
|
|
91
|
+
export declare function buildHealthDcatFederationRequest(input: ControllerAuthority & Readonly<{
|
|
92
|
+
catalogIdentifier: string;
|
|
93
|
+
hostServiceProviderIdentifier: string;
|
|
94
|
+
destinationDataspaceIdentifier: string;
|
|
95
|
+
}>): Readonly<{
|
|
96
|
+
catalogIdentifier: string;
|
|
97
|
+
publisherIdentifier: string;
|
|
98
|
+
hostServiceProviderIdentifier: string;
|
|
99
|
+
destinationDataspaceIdentifier: string;
|
|
100
|
+
authorizedBy: string;
|
|
101
|
+
}>;
|
|
102
|
+
/** Advertises a host catalogue endpoint in ICA without leaking tenant inventory. */
|
|
103
|
+
export declare function buildCatalogHostAdvertisement(input: Readonly<{
|
|
104
|
+
hostIdentifier: string;
|
|
105
|
+
catalogEndpoint: string;
|
|
106
|
+
tenantDiscovery: 'public' | 'openid4vp';
|
|
107
|
+
}>): Readonly<{
|
|
108
|
+
hostIdentifier: string;
|
|
109
|
+
catalogEndpoint: string;
|
|
110
|
+
catalogProtocol: 'dataspace-protocol-2025-1';
|
|
111
|
+
tenantDiscovery: 'public' | 'openid4vp';
|
|
112
|
+
}>;
|
|
113
|
+
/** References standard trust and presentation protocols for restricted catalogue discovery. */
|
|
114
|
+
export declare function buildCatalogDiscoveryPolicy(input: Readonly<{
|
|
115
|
+
visibility: 'public' | 'trusted-federation' | 'private';
|
|
116
|
+
trustAnchorIdentifiers?: readonly string[];
|
|
117
|
+
presentationDefinitionUri?: string;
|
|
118
|
+
}>): Readonly<{
|
|
119
|
+
visibility: 'public' | 'trusted-federation' | 'private';
|
|
120
|
+
trustResolution?: 'openid-federation-1.0';
|
|
121
|
+
presentationProtocol?: 'openid4vp-1.0';
|
|
122
|
+
trustAnchorIdentifiers?: readonly string[];
|
|
123
|
+
presentationDefinitionUri?: string;
|
|
124
|
+
}>;
|
|
125
|
+
export {};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Copyright 2026 Connecting Solution & Applications Ltd under the Apache License, Version 2.0.
|
|
2
|
+
/** Product sectors whose organization UI may expose HealthDCAT publication. */
|
|
3
|
+
export const HealthDcatPublisherSectors = Object.freeze([
|
|
4
|
+
'public-health',
|
|
5
|
+
'animal-care', 'health-care',
|
|
6
|
+
'animal-insurance', 'health-insurance', 'onehealth-insurance',
|
|
7
|
+
'animal-lab', 'health-lab', 'onehealth-lab',
|
|
8
|
+
'animal-research', 'health-research', 'onehealth-research',
|
|
9
|
+
]);
|
|
10
|
+
/** Standards-derived flat Dataset claims for indexed product persistence. */
|
|
11
|
+
export var ClaimsHealthDcatDataset;
|
|
12
|
+
(function (ClaimsHealthDcatDataset) {
|
|
13
|
+
ClaimsHealthDcatDataset["identifier"] = "dct.identifier";
|
|
14
|
+
ClaimsHealthDcatDataset["title"] = "dct.title";
|
|
15
|
+
ClaimsHealthDcatDataset["description"] = "dct.description";
|
|
16
|
+
ClaimsHealthDcatDataset["creator"] = "dct.creator";
|
|
17
|
+
ClaimsHealthDcatDataset["publisher"] = "dct.publisher";
|
|
18
|
+
ClaimsHealthDcatDataset["custodian"] = "geodcatap.custodian";
|
|
19
|
+
ClaimsHealthDcatDataset["accessRights"] = "dct.accessRights";
|
|
20
|
+
ClaimsHealthDcatDataset["applicableLegislation"] = "dcatap.applicableLegislation";
|
|
21
|
+
ClaimsHealthDcatDataset["distribution"] = "dcat.distribution";
|
|
22
|
+
ClaimsHealthDcatDataset["source"] = "dct.source";
|
|
23
|
+
ClaimsHealthDcatDataset["provenance"] = "dct.provenance";
|
|
24
|
+
ClaimsHealthDcatDataset["personalData"] = "dpv.hasPersonalData";
|
|
25
|
+
ClaimsHealthDcatDataset["healthCategory"] = "healthdcatap.healthCategory";
|
|
26
|
+
ClaimsHealthDcatDataset["healthDataAccessBody"] = "healthdcatap.hdab";
|
|
27
|
+
ClaimsHealthDcatDataset["structuredData"] = "healthdcatap.hasStructuredData";
|
|
28
|
+
ClaimsHealthDcatDataset["variables"] = "healthdcatap.hasVariables";
|
|
29
|
+
ClaimsHealthDcatDataset["authorizationReference"] = "prov.qualifiedAttribution";
|
|
30
|
+
ClaimsHealthDcatDataset["userSelected"] = "Dataset.user-selected";
|
|
31
|
+
})(ClaimsHealthDcatDataset || (ClaimsHealthDcatDataset = {}));
|
|
32
|
+
/** Standards-derived flat Catalogue claims for indexed product persistence. */
|
|
33
|
+
export var ClaimsHealthDcatCatalog;
|
|
34
|
+
(function (ClaimsHealthDcatCatalog) {
|
|
35
|
+
ClaimsHealthDcatCatalog["identifier"] = "dct.identifier";
|
|
36
|
+
ClaimsHealthDcatCatalog["title"] = "dct.title";
|
|
37
|
+
ClaimsHealthDcatCatalog["description"] = "dct.description";
|
|
38
|
+
ClaimsHealthDcatCatalog["creator"] = "dct.creator";
|
|
39
|
+
ClaimsHealthDcatCatalog["publisher"] = "dct.publisher";
|
|
40
|
+
ClaimsHealthDcatCatalog["applicableLegislation"] = "dcatap.applicableLegislation";
|
|
41
|
+
ClaimsHealthDcatCatalog["dataset"] = "dcat.dataset";
|
|
42
|
+
ClaimsHealthDcatCatalog["userSelected"] = "Catalog.user-selected";
|
|
43
|
+
})(ClaimsHealthDcatCatalog || (ClaimsHealthDcatCatalog = {}));
|
|
44
|
+
/** Complete allowlist for product-persisted HealthDCAT flat claims. */
|
|
45
|
+
export const HealthDcatFlatClaimCatalog = Object.freeze({
|
|
46
|
+
Dataset: Object.freeze(Object.values(ClaimsHealthDcatDataset)),
|
|
47
|
+
Catalog: Object.freeze(Object.values(ClaimsHealthDcatCatalog)),
|
|
48
|
+
});
|
|
49
|
+
/** Validates an indexed HealthDCAT resource and rejects nested or unknown claims. */
|
|
50
|
+
export function normalizeHealthDcatResource(candidate) {
|
|
51
|
+
if (!candidate || typeof candidate !== 'object')
|
|
52
|
+
throw new TypeError('health_dcat_resource_invalid');
|
|
53
|
+
const value = candidate;
|
|
54
|
+
if (value.resourceType !== 'Dataset' && value.resourceType !== 'Catalog')
|
|
55
|
+
throw new TypeError('health_dcat_resource_invalid');
|
|
56
|
+
if (typeof value.id !== 'string' || !value.id.trim())
|
|
57
|
+
throw new TypeError('health_dcat_resource_invalid');
|
|
58
|
+
if (!value.meta || typeof value.meta !== 'object' || Array.isArray(value.meta))
|
|
59
|
+
throw new TypeError('health_dcat_resource_invalid');
|
|
60
|
+
const claims = value.meta.claims;
|
|
61
|
+
if (!claims || typeof claims !== 'object' || Array.isArray(claims))
|
|
62
|
+
throw new TypeError('health_dcat_resource_invalid');
|
|
63
|
+
const allowed = HealthDcatFlatClaimCatalog[value.resourceType];
|
|
64
|
+
const normalized = {};
|
|
65
|
+
for (const [claim, raw] of Object.entries(claims)) {
|
|
66
|
+
if (!allowed.includes(claim))
|
|
67
|
+
throw new TypeError(`health_dcat_claim_not_governed:${claim}`);
|
|
68
|
+
if (typeof raw === 'string' || typeof raw === 'boolean' || typeof raw === 'number')
|
|
69
|
+
normalized[claim] = raw;
|
|
70
|
+
else if (Array.isArray(raw) && raw.every(item => typeof item === 'string'))
|
|
71
|
+
normalized[claim] = Object.freeze([...raw]);
|
|
72
|
+
else
|
|
73
|
+
throw new TypeError(`health_dcat_claim_value_invalid:${claim}`);
|
|
74
|
+
}
|
|
75
|
+
return Object.freeze({
|
|
76
|
+
resourceType: value.resourceType,
|
|
77
|
+
id: value.id,
|
|
78
|
+
meta: Object.freeze({ claims: Object.freeze(normalized) }),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const JsonLdClaimNames = Object.freeze({
|
|
82
|
+
[ClaimsHealthDcatDataset.identifier]: 'dct:identifier',
|
|
83
|
+
[ClaimsHealthDcatDataset.title]: 'dct:title',
|
|
84
|
+
[ClaimsHealthDcatDataset.description]: 'dct:description',
|
|
85
|
+
[ClaimsHealthDcatDataset.creator]: 'dct:creator',
|
|
86
|
+
[ClaimsHealthDcatDataset.publisher]: 'dct:publisher',
|
|
87
|
+
[ClaimsHealthDcatDataset.custodian]: 'geodcatap:custodian',
|
|
88
|
+
[ClaimsHealthDcatDataset.accessRights]: 'dct:accessRights',
|
|
89
|
+
[ClaimsHealthDcatDataset.applicableLegislation]: 'dcatap:applicableLegislation',
|
|
90
|
+
[ClaimsHealthDcatDataset.distribution]: 'dcat:distribution',
|
|
91
|
+
[ClaimsHealthDcatDataset.source]: 'dct:source',
|
|
92
|
+
[ClaimsHealthDcatDataset.provenance]: 'dct:provenance',
|
|
93
|
+
[ClaimsHealthDcatDataset.personalData]: 'dpv:hasPersonalData',
|
|
94
|
+
[ClaimsHealthDcatDataset.healthCategory]: 'healthdcatap:healthCategory',
|
|
95
|
+
[ClaimsHealthDcatDataset.healthDataAccessBody]: 'healthdcatap:hdab',
|
|
96
|
+
[ClaimsHealthDcatDataset.structuredData]: 'healthdcatap:hasStructuredData',
|
|
97
|
+
[ClaimsHealthDcatDataset.variables]: 'healthdcatap:hasVariables',
|
|
98
|
+
[ClaimsHealthDcatDataset.authorizationReference]: 'prov:qualifiedAttribution',
|
|
99
|
+
[ClaimsHealthDcatCatalog.dataset]: 'dcat:dataset',
|
|
100
|
+
});
|
|
101
|
+
/** Explicitly projects internal flat claims to DCAT/HealthDCAT JSON-LD. */
|
|
102
|
+
export function projectHealthDcatResourceToJsonLd(resource) {
|
|
103
|
+
const normalized = normalizeHealthDcatResource(resource);
|
|
104
|
+
const output = {
|
|
105
|
+
'@context': Object.freeze({
|
|
106
|
+
dcat: 'http://www.w3.org/ns/dcat#',
|
|
107
|
+
dct: 'http://purl.org/dc/terms/',
|
|
108
|
+
dcatap: 'http://data.europa.eu/r5r/',
|
|
109
|
+
geodcatap: 'http://data.europa.eu/930/',
|
|
110
|
+
healthdcatap: 'http://healthdataportal.eu/ns/health#',
|
|
111
|
+
dpv: 'https://w3id.org/dpv#',
|
|
112
|
+
prov: 'http://www.w3.org/ns/prov#',
|
|
113
|
+
}),
|
|
114
|
+
'@id': normalized.id,
|
|
115
|
+
'@type': normalized.resourceType === 'Catalog' ? 'dcat:Catalog' : 'dcat:Dataset',
|
|
116
|
+
};
|
|
117
|
+
for (const [claim, value] of Object.entries(normalized.meta.claims)) {
|
|
118
|
+
const jsonLdName = JsonLdClaimNames[claim];
|
|
119
|
+
if (jsonLdName)
|
|
120
|
+
output[jsonLdName] = value;
|
|
121
|
+
}
|
|
122
|
+
return Object.freeze(output);
|
|
123
|
+
}
|
|
124
|
+
function required(value, field) {
|
|
125
|
+
const normalized = value.trim();
|
|
126
|
+
if (!normalized)
|
|
127
|
+
throw new Error(`${field} is required`);
|
|
128
|
+
return normalized;
|
|
129
|
+
}
|
|
130
|
+
function assertController(input) {
|
|
131
|
+
if (required(input.actingControllerIdentifier, 'actingControllerIdentifier') !==
|
|
132
|
+
required(input.controllerIdentifier, 'controllerIdentifier')) {
|
|
133
|
+
throw new Error('Only the organization controller may perform this operation');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** Builds a claims-first HealthDCAT Dataset description under explicit tenant authority. */
|
|
137
|
+
export function buildHealthDcatDataset(input) {
|
|
138
|
+
assertController(input);
|
|
139
|
+
if (!HealthDcatPublisherSectors.includes(input.sector)) {
|
|
140
|
+
throw new Error(`Sector ${input.sector} cannot publish HealthDCAT metadata`);
|
|
141
|
+
}
|
|
142
|
+
if (input.publisherRole === 'authorized-intermediary' && !input.authorizationReference?.trim()) {
|
|
143
|
+
throw new Error('authorizationReference is required for an authorized intermediary');
|
|
144
|
+
}
|
|
145
|
+
if (input.healthCategory.length === 0)
|
|
146
|
+
throw new Error('healthCategory is required');
|
|
147
|
+
if (input.applicableLegislation.length === 0)
|
|
148
|
+
throw new Error('applicableLegislation is required');
|
|
149
|
+
if (input.accessRights === 'NON_PUBLIC' && input.distributionIdentifiers.length === 0) {
|
|
150
|
+
throw new Error('A non-public Dataset requires an HDAB Distribution');
|
|
151
|
+
}
|
|
152
|
+
if (input.structuredData && !input.variableTableGroupIdentifiers?.length) {
|
|
153
|
+
throw new Error('Structured data requires variableTableGroupIdentifiers');
|
|
154
|
+
}
|
|
155
|
+
const organizationIdentifier = required(input.organizationIdentifier, 'organizationIdentifier');
|
|
156
|
+
const sourceReference = required(input.sourceReference, 'sourceReference');
|
|
157
|
+
const claims = {
|
|
158
|
+
[ClaimsHealthDcatDataset.identifier]: required(input.id, 'id'),
|
|
159
|
+
[ClaimsHealthDcatDataset.title]: required(input.title, 'title'),
|
|
160
|
+
[ClaimsHealthDcatDataset.description]: required(input.description, 'description'),
|
|
161
|
+
[ClaimsHealthDcatDataset.creator]: input.controllerIdentifier,
|
|
162
|
+
[ClaimsHealthDcatDataset.publisher]: organizationIdentifier,
|
|
163
|
+
[ClaimsHealthDcatDataset.custodian]: required(input.dataHolderIdentifier, 'dataHolderIdentifier'),
|
|
164
|
+
[ClaimsHealthDcatDataset.accessRights]: input.accessRights,
|
|
165
|
+
[ClaimsHealthDcatDataset.applicableLegislation]: Object.freeze([...input.applicableLegislation]),
|
|
166
|
+
[ClaimsHealthDcatDataset.distribution]: Object.freeze([...input.distributionIdentifiers]),
|
|
167
|
+
[ClaimsHealthDcatDataset.source]: sourceReference,
|
|
168
|
+
[ClaimsHealthDcatDataset.provenance]: `${input.sourceKind}:${sourceReference}`,
|
|
169
|
+
[ClaimsHealthDcatDataset.personalData]: input.anonymizationStatus !== 'anonymous',
|
|
170
|
+
[ClaimsHealthDcatDataset.healthCategory]: Object.freeze([...input.healthCategory]),
|
|
171
|
+
[ClaimsHealthDcatDataset.healthDataAccessBody]: required(input.healthDataAccessBodyIdentifier, 'healthDataAccessBodyIdentifier'),
|
|
172
|
+
[ClaimsHealthDcatDataset.structuredData]: input.structuredData,
|
|
173
|
+
[ClaimsHealthDcatDataset.userSelected]: true,
|
|
174
|
+
};
|
|
175
|
+
if (input.variableTableGroupIdentifiers?.length) {
|
|
176
|
+
claims[ClaimsHealthDcatDataset.variables] = Object.freeze([...input.variableTableGroupIdentifiers]);
|
|
177
|
+
}
|
|
178
|
+
if (input.authorizationReference) {
|
|
179
|
+
claims[ClaimsHealthDcatDataset.authorizationReference] = input.authorizationReference;
|
|
180
|
+
}
|
|
181
|
+
return Object.freeze({ resourceType: 'Dataset', id: input.id, meta: Object.freeze({ claims: Object.freeze(claims) }) });
|
|
182
|
+
}
|
|
183
|
+
/** Builds the tenant-owned marketplace Catalogue; creation is controller-only. */
|
|
184
|
+
export function buildHealthDcatCatalog(input) {
|
|
185
|
+
assertController(input);
|
|
186
|
+
if (input.applicableLegislation.length === 0)
|
|
187
|
+
throw new Error('applicableLegislation is required');
|
|
188
|
+
const claims = {
|
|
189
|
+
[ClaimsHealthDcatCatalog.identifier]: required(input.id, 'id'),
|
|
190
|
+
[ClaimsHealthDcatCatalog.title]: required(input.title, 'title'),
|
|
191
|
+
[ClaimsHealthDcatCatalog.description]: required(input.description, 'description'),
|
|
192
|
+
[ClaimsHealthDcatCatalog.creator]: input.controllerIdentifier,
|
|
193
|
+
[ClaimsHealthDcatCatalog.publisher]: required(input.organizationIdentifier, 'organizationIdentifier'),
|
|
194
|
+
[ClaimsHealthDcatCatalog.applicableLegislation]: Object.freeze([...input.applicableLegislation]),
|
|
195
|
+
[ClaimsHealthDcatCatalog.dataset]: Object.freeze([...input.datasetIdentifiers]),
|
|
196
|
+
[ClaimsHealthDcatCatalog.userSelected]: true,
|
|
197
|
+
};
|
|
198
|
+
return Object.freeze({ resourceType: 'Catalog', id: input.id, meta: Object.freeze({ claims: Object.freeze(claims) }) });
|
|
199
|
+
}
|
|
200
|
+
/** Creates an auditable tenant authorization for host-provided catalogue federation. */
|
|
201
|
+
export function buildHealthDcatFederationRequest(input) {
|
|
202
|
+
assertController(input);
|
|
203
|
+
return Object.freeze({
|
|
204
|
+
catalogIdentifier: required(input.catalogIdentifier, 'catalogIdentifier'),
|
|
205
|
+
publisherIdentifier: required(input.organizationIdentifier, 'organizationIdentifier'),
|
|
206
|
+
hostServiceProviderIdentifier: required(input.hostServiceProviderIdentifier, 'hostServiceProviderIdentifier'),
|
|
207
|
+
destinationDataspaceIdentifier: required(input.destinationDataspaceIdentifier, 'destinationDataspaceIdentifier'),
|
|
208
|
+
authorizedBy: input.controllerIdentifier,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/** Advertises a host catalogue endpoint in ICA without leaking tenant inventory. */
|
|
212
|
+
export function buildCatalogHostAdvertisement(input) {
|
|
213
|
+
const endpoint = required(input.catalogEndpoint, 'catalogEndpoint');
|
|
214
|
+
if (!endpoint.startsWith('https://'))
|
|
215
|
+
throw new Error('catalogEndpoint must use HTTPS');
|
|
216
|
+
return Object.freeze({
|
|
217
|
+
hostIdentifier: required(input.hostIdentifier, 'hostIdentifier'),
|
|
218
|
+
catalogEndpoint: endpoint,
|
|
219
|
+
catalogProtocol: 'dataspace-protocol-2025-1',
|
|
220
|
+
tenantDiscovery: input.tenantDiscovery,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/** References standard trust and presentation protocols for restricted catalogue discovery. */
|
|
224
|
+
export function buildCatalogDiscoveryPolicy(input) {
|
|
225
|
+
if (input.visibility === 'trusted-federation') {
|
|
226
|
+
if (!input.trustAnchorIdentifiers?.length)
|
|
227
|
+
throw new Error('trustAnchorIdentifiers are required');
|
|
228
|
+
const presentationDefinitionUri = required(input.presentationDefinitionUri ?? '', 'presentationDefinitionUri');
|
|
229
|
+
if (!presentationDefinitionUri.startsWith('https://'))
|
|
230
|
+
throw new Error('presentationDefinitionUri must use HTTPS');
|
|
231
|
+
return Object.freeze({
|
|
232
|
+
visibility: input.visibility,
|
|
233
|
+
trustResolution: 'openid-federation-1.0',
|
|
234
|
+
presentationProtocol: 'openid4vp-1.0',
|
|
235
|
+
trustAnchorIdentifiers: Object.freeze([...input.trustAnchorIdentifiers]),
|
|
236
|
+
presentationDefinitionUri,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return Object.freeze({ visibility: input.visibility });
|
|
240
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './digital-twin.js';
|
|
|
7
7
|
export * from './emergency.js';
|
|
8
8
|
export * from './financial.js';
|
|
9
9
|
export * from './group.js';
|
|
10
|
+
export * from './health-dcat.js';
|
|
10
11
|
export * from './immunization.js';
|
|
11
12
|
export * from './international-health-card.js';
|
|
12
13
|
export * from './index-projection-tags.js';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export * from './digital-twin.js';
|
|
|
7
7
|
export * from './emergency.js';
|
|
8
8
|
export * from './financial.js';
|
|
9
9
|
export * from './group.js';
|
|
10
|
+
export * from './health-dcat.js';
|
|
10
11
|
export * from './immunization.js';
|
|
11
12
|
export * from './international-health-card.js';
|
|
12
13
|
export * from './index-projection-tags.js';
|
package/dist/sectors.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* the value grants no organization role, membership or data capability.
|
|
7
7
|
*/
|
|
8
8
|
export declare const ExtendedDataspaceSectors: Readonly<{
|
|
9
|
+
readonly PublicHealth: "public-health";
|
|
9
10
|
readonly PublicSafety: "public-safety";
|
|
10
11
|
readonly HealthCare: "health-care";
|
|
11
12
|
readonly HealthLab: "health-lab";
|
package/dist/sectors.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vet-data-utils-ts",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.25",
|
|
4
4
|
"description": "Browser-safe governed VetChain data contracts",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Connecting Solution & Applications Ltd",
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"types": "./dist/group.d.ts",
|
|
45
45
|
"default": "./dist/group.js"
|
|
46
46
|
},
|
|
47
|
+
"./health-dcat": {
|
|
48
|
+
"types": "./dist/health-dcat.d.ts",
|
|
49
|
+
"default": "./dist/health-dcat.js"
|
|
50
|
+
},
|
|
47
51
|
"./immunization": {
|
|
48
52
|
"types": "./dist/immunization.d.ts",
|
|
49
53
|
"default": "./dist/immunization.js"
|