gdc-common-utils-ts 1.14.2 → 1.14.3
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
|
@@ -111,6 +111,8 @@ Main entry points:
|
|
|
111
111
|
- [`src/utils/dataspace-discovery-defaults.ts`](src/utils/dataspace-discovery-defaults.ts)
|
|
112
112
|
- defaults registry for ICAs and hosting operators plus the backend
|
|
113
113
|
`default-first` bootstrap plan used to unblock portal integration
|
|
114
|
+
- includes authority-based helpers so integrators can seed from a single
|
|
115
|
+
domain/IP instead of manually assembling `did:web` and discovery URLs
|
|
114
116
|
- [`src/examples/dataspace-discovery.ts`](src/examples/dataspace-discovery.ts)
|
|
115
117
|
- synthetic provider/operator examples that distinguish discovery URL from
|
|
116
118
|
derived catalog artifact URL
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DataspaceDiscoverySourceModeValue } from '../constants/dataspace-discovery';
|
|
2
|
-
import type { HostingOperatorSemanticRecord } from './dataspace-discovery';
|
|
2
|
+
import type { HostingOperatorSemanticRecord, PublishedProviderCatalogRecord } from './dataspace-discovery';
|
|
3
3
|
/**
|
|
4
4
|
* Shared host/ICA context used to select dataspace discovery defaults.
|
|
5
5
|
*
|
|
@@ -29,6 +29,7 @@ export type DefaultHostingOperatorRegistration = DataspaceDiscoveryNetworkContex
|
|
|
29
29
|
discoveryUrl?: string;
|
|
30
30
|
catalogUrl?: string;
|
|
31
31
|
record: HostingOperatorSemanticRecord;
|
|
32
|
+
publishedProviders?: readonly PublishedProviderCatalogRecord[];
|
|
32
33
|
title?: string;
|
|
33
34
|
}>;
|
|
34
35
|
/**
|
|
@@ -1,4 +1,68 @@
|
|
|
1
1
|
import type { DataspaceDiscoveryBootstrapInput, DataspaceDiscoveryBootstrapPlan, DataspaceDiscoveryDefaultHostingFilter, DataspaceDiscoveryDefaultIcaFilter, DataspaceDiscoveryDefaultsRegistrySeed, DefaultHostingOperatorRegistration, DefaultIcaRegistration } from '../models/dataspace-discovery-defaults';
|
|
2
|
+
import type { PublishedProviderCatalogRecord } from '../models/dataspace-discovery';
|
|
3
|
+
export type BuildDefaultIcaRegistrationFromAuthorityInput = Readonly<{
|
|
4
|
+
authority: string;
|
|
5
|
+
jurisdiction: string;
|
|
6
|
+
version: string;
|
|
7
|
+
networkType: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
}>;
|
|
10
|
+
export type BuildDefaultHostingOperatorRegistrationFromAuthorityInput = Readonly<{
|
|
11
|
+
authority: string;
|
|
12
|
+
jurisdiction: string;
|
|
13
|
+
version: string;
|
|
14
|
+
networkType: string;
|
|
15
|
+
sector: string;
|
|
16
|
+
serviceTypes: readonly string[];
|
|
17
|
+
title?: string;
|
|
18
|
+
areaServed?: readonly string[];
|
|
19
|
+
addressCountry?: string;
|
|
20
|
+
coverageScope?: string;
|
|
21
|
+
}>;
|
|
22
|
+
export type BuildDefaultPublishedProviderRecordFromTenantInput = Readonly<{
|
|
23
|
+
hostAuthority: string;
|
|
24
|
+
tenantId: string;
|
|
25
|
+
jurisdiction: string;
|
|
26
|
+
version: string;
|
|
27
|
+
sector: string;
|
|
28
|
+
providerCapability: string;
|
|
29
|
+
areaServed?: readonly string[];
|
|
30
|
+
/**
|
|
31
|
+
* Optional public domain for a future externally visible provider surface.
|
|
32
|
+
*
|
|
33
|
+
* For now most deployments will not use it, so the helper falls back to the
|
|
34
|
+
* hosted/internal tenant route under the hosting operator domain.
|
|
35
|
+
*/
|
|
36
|
+
externalDomain?: string;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Builds one ICA default seed from a domain/IP authority.
|
|
40
|
+
*
|
|
41
|
+
* This helper exists so integrators do not have to handcraft:
|
|
42
|
+
* - the `did:web`
|
|
43
|
+
* - the well-known ICA URL
|
|
44
|
+
* - the repeated host/network context fields
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildDefaultIcaRegistrationFromAuthority(input: BuildDefaultIcaRegistrationFromAuthorityInput): DefaultIcaRegistration;
|
|
47
|
+
/**
|
|
48
|
+
* Builds one hosting-operator default seed from a domain/IP authority.
|
|
49
|
+
*
|
|
50
|
+
* This helper exists so integrators do not have to handcraft:
|
|
51
|
+
* - the `did:web`
|
|
52
|
+
* - the host-scoped DSP discovery URL
|
|
53
|
+
* - the semantic host record shape
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildDefaultHostingOperatorRegistrationFromAuthority(input: BuildDefaultHostingOperatorRegistrationFromAuthorityInput): DefaultHostingOperatorRegistration;
|
|
56
|
+
/**
|
|
57
|
+
* Builds one published-provider default entry from a tenant id under a given
|
|
58
|
+
* hosting operator authority.
|
|
59
|
+
*
|
|
60
|
+
* Integrator-friendly rule:
|
|
61
|
+
* - the backend usually knows `tenantId`
|
|
62
|
+
* - it usually does not want to handcraft the hosted/internal `did:web`
|
|
63
|
+
* - this helper derives the hosted provider DID and tenant-scoped DSP URLs
|
|
64
|
+
*/
|
|
65
|
+
export declare function buildDefaultPublishedProviderRecordFromTenant(input: BuildDefaultPublishedProviderRecordFromTenantInput): PublishedProviderCatalogRecord;
|
|
2
66
|
/**
|
|
3
67
|
* Mutable in-memory registry for discovery defaults used by portal/backend
|
|
4
68
|
* bootstrap code.
|
|
@@ -1,12 +1,57 @@
|
|
|
1
1
|
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
2
|
import { DataspaceDiscoverySourceMode, } from '../constants/dataspace-discovery.js';
|
|
3
|
+
import { normalizeCountryCode } from '../constants/eu-countries.js';
|
|
4
|
+
import { buildOrganizationDidWeb, getBaseUrlFromDidWeb } from './did.js';
|
|
3
5
|
import { matchesHostingOperatorDiscoveryFilter } from './dataspace-discovery.js';
|
|
4
6
|
function normalizeString(value) {
|
|
5
7
|
return typeof value === 'string' ? value.trim() : '';
|
|
6
8
|
}
|
|
9
|
+
function normalizeAuthority(value) {
|
|
10
|
+
const raw = normalizeString(value).replace(/\/+$/, '');
|
|
11
|
+
if (!raw)
|
|
12
|
+
return '';
|
|
13
|
+
if (/^https?:\/\//i.test(raw)) {
|
|
14
|
+
try {
|
|
15
|
+
return new URL(raw).host;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return raw.replace(/^https?:\/\//i, '');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return raw;
|
|
22
|
+
}
|
|
23
|
+
function getProtocolForAuthority(authority) {
|
|
24
|
+
const normalized = authority.trim().toLowerCase();
|
|
25
|
+
return normalized.startsWith('localhost')
|
|
26
|
+
|| normalized.startsWith('127.0.0.1')
|
|
27
|
+
|| normalized.startsWith('[::1]')
|
|
28
|
+
? 'http'
|
|
29
|
+
: 'https';
|
|
30
|
+
}
|
|
31
|
+
function encodeDidWebAuthority(authority) {
|
|
32
|
+
return authority.replace(/:/g, '%3A').toLowerCase();
|
|
33
|
+
}
|
|
34
|
+
function buildDidWebFromAuthority(authority) {
|
|
35
|
+
return `did:web:${encodeDidWebAuthority(authority)}`;
|
|
36
|
+
}
|
|
7
37
|
function normalizeCapabilities(values) {
|
|
8
38
|
return Array.from(new Set((values || []).map((value) => normalizeString(value)).filter(Boolean)));
|
|
9
39
|
}
|
|
40
|
+
function normalizePublishedProvider(input) {
|
|
41
|
+
return {
|
|
42
|
+
providerDid: normalizeString(input.providerDid),
|
|
43
|
+
serviceType: normalizeString(input.serviceType),
|
|
44
|
+
category: normalizeString(input.category),
|
|
45
|
+
areaServed: normalizeCapabilities(Array.isArray(input.areaServed)
|
|
46
|
+
? input.areaServed
|
|
47
|
+
: normalizeString(input.areaServed)
|
|
48
|
+
? normalizeString(input.areaServed).split(',')
|
|
49
|
+
: undefined).join(',') || undefined,
|
|
50
|
+
endpointUrl: normalizeString(input.endpointUrl) || undefined,
|
|
51
|
+
discoveryUrl: normalizeString(input.discoveryUrl) || undefined,
|
|
52
|
+
catalogUrl: normalizeString(input.catalogUrl) || undefined,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
10
55
|
function normalizeIcaRegistration(input) {
|
|
11
56
|
return {
|
|
12
57
|
jurisdiction: normalizeString(input.jurisdiction),
|
|
@@ -34,6 +79,7 @@ function normalizeHostingOperatorRegistration(input) {
|
|
|
34
79
|
addressCountry: normalizeString(input.record.addressCountry) || undefined,
|
|
35
80
|
coverageScope: normalizeString(input.record.coverageScope) || undefined,
|
|
36
81
|
},
|
|
82
|
+
publishedProviders: (input.publishedProviders || []).map((provider) => normalizePublishedProvider(provider)),
|
|
37
83
|
title: normalizeString(input.title) || undefined,
|
|
38
84
|
};
|
|
39
85
|
}
|
|
@@ -46,6 +92,92 @@ function matchesNetworkContext(left, right) {
|
|
|
46
92
|
return false;
|
|
47
93
|
return true;
|
|
48
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Builds one ICA default seed from a domain/IP authority.
|
|
97
|
+
*
|
|
98
|
+
* This helper exists so integrators do not have to handcraft:
|
|
99
|
+
* - the `did:web`
|
|
100
|
+
* - the well-known ICA URL
|
|
101
|
+
* - the repeated host/network context fields
|
|
102
|
+
*/
|
|
103
|
+
export function buildDefaultIcaRegistrationFromAuthority(input) {
|
|
104
|
+
const authority = normalizeAuthority(input.authority);
|
|
105
|
+
const protocol = getProtocolForAuthority(authority);
|
|
106
|
+
return normalizeIcaRegistration({
|
|
107
|
+
jurisdiction: input.jurisdiction,
|
|
108
|
+
version: input.version,
|
|
109
|
+
networkType: input.networkType,
|
|
110
|
+
title: input.title,
|
|
111
|
+
icaUrl: `${protocol}://${authority}/.well-known/ica-configuration`,
|
|
112
|
+
icaDid: buildDidWebFromAuthority(authority),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Builds one hosting-operator default seed from a domain/IP authority.
|
|
117
|
+
*
|
|
118
|
+
* This helper exists so integrators do not have to handcraft:
|
|
119
|
+
* - the `did:web`
|
|
120
|
+
* - the host-scoped DSP discovery URL
|
|
121
|
+
* - the semantic host record shape
|
|
122
|
+
*/
|
|
123
|
+
export function buildDefaultHostingOperatorRegistrationFromAuthority(input) {
|
|
124
|
+
const authority = normalizeAuthority(input.authority);
|
|
125
|
+
const protocol = getProtocolForAuthority(authority);
|
|
126
|
+
const addressCountry = normalizeCountryCode(input.addressCountry) || normalizeCountryCode(input.jurisdiction);
|
|
127
|
+
const coverageScope = normalizeString(input.coverageScope) || undefined;
|
|
128
|
+
const areaServed = Array.from(new Set((input.areaServed || [input.jurisdiction])
|
|
129
|
+
.map((value) => normalizeString(value))
|
|
130
|
+
.filter(Boolean)));
|
|
131
|
+
return normalizeHostingOperatorRegistration({
|
|
132
|
+
jurisdiction: input.jurisdiction,
|
|
133
|
+
version: input.version,
|
|
134
|
+
networkType: input.networkType,
|
|
135
|
+
title: input.title,
|
|
136
|
+
operatorDid: buildDidWebFromAuthority(authority),
|
|
137
|
+
discoveryUrl: `${protocol}://${authority}/host/cds-${input.jurisdiction}/${input.version}/${input.networkType}/.well-known/dspace-version`,
|
|
138
|
+
record: {
|
|
139
|
+
subjectId: buildDidWebFromAuthority(authority),
|
|
140
|
+
serviceTypes: [...input.serviceTypes],
|
|
141
|
+
categories: [normalizeString(input.sector)],
|
|
142
|
+
areaServed,
|
|
143
|
+
addressCountry: addressCountry || undefined,
|
|
144
|
+
coverageScope,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Builds one published-provider default entry from a tenant id under a given
|
|
150
|
+
* hosting operator authority.
|
|
151
|
+
*
|
|
152
|
+
* Integrator-friendly rule:
|
|
153
|
+
* - the backend usually knows `tenantId`
|
|
154
|
+
* - it usually does not want to handcraft the hosted/internal `did:web`
|
|
155
|
+
* - this helper derives the hosted provider DID and tenant-scoped DSP URLs
|
|
156
|
+
*/
|
|
157
|
+
export function buildDefaultPublishedProviderRecordFromTenant(input) {
|
|
158
|
+
const hostAuthority = normalizeAuthority(input.hostAuthority);
|
|
159
|
+
const hostDidWeb = buildDidWebFromAuthority(hostAuthority);
|
|
160
|
+
const providerDid = buildOrganizationDidWeb({
|
|
161
|
+
hostDidWeb,
|
|
162
|
+
tenantId: input.tenantId,
|
|
163
|
+
jurisdiction: input.jurisdiction,
|
|
164
|
+
version: input.version,
|
|
165
|
+
sector: input.sector,
|
|
166
|
+
});
|
|
167
|
+
const providerBaseUrl = normalizeAuthority(input.externalDomain)
|
|
168
|
+
? `${getProtocolForAuthority(normalizeAuthority(input.externalDomain))}://${normalizeAuthority(input.externalDomain)}/`
|
|
169
|
+
: getBaseUrlFromDidWeb(providerDid);
|
|
170
|
+
const areaServed = normalizeCapabilities((input.areaServed || [input.jurisdiction]).map((value) => normalizeString(value))).join(',') || undefined;
|
|
171
|
+
return normalizePublishedProvider({
|
|
172
|
+
providerDid,
|
|
173
|
+
serviceType: input.providerCapability,
|
|
174
|
+
category: input.sector,
|
|
175
|
+
areaServed,
|
|
176
|
+
endpointUrl: providerBaseUrl,
|
|
177
|
+
discoveryUrl: new URL('.well-known/dspace-version', providerBaseUrl).toString(),
|
|
178
|
+
catalogUrl: new URL('dsp/catalog/dcat.json', providerBaseUrl).toString(),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
49
181
|
/**
|
|
50
182
|
* Mutable in-memory registry for discovery defaults used by portal/backend
|
|
51
183
|
* bootstrap code.
|
|
@@ -133,6 +265,9 @@ export class DataspaceDiscoveryDefaultsRegistry {
|
|
|
133
265
|
categories: [...registration.record.categories],
|
|
134
266
|
areaServed: [...registration.record.areaServed],
|
|
135
267
|
},
|
|
268
|
+
publishedProviders: (registration.publishedProviders || []).map((provider) => ({
|
|
269
|
+
...provider,
|
|
270
|
+
})),
|
|
136
271
|
}));
|
|
137
272
|
}
|
|
138
273
|
/**
|