gdc-sdk-node-ts 0.5.0 → 0.6.0

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
@@ -25,12 +25,15 @@ If you are integrating this package for the first time, open these in order:
25
25
  Real backend setup, imports, `initializeCommunicationIdentity(...)`,
26
26
  `new NodeHttpClient(...)`, route context, facade selection, and live method
27
27
  usage.
28
- 3. [gdc-sdk-core-ts/docs/SDK_FLOWS_101.md](https://github.com/Global-DataCare/gdc-sdk-core-ts/blob/main/docs/SDK_FLOWS_101.md)
28
+ 3. [docs/DISCOVERY_101.md](./docs/DISCOVERY_101.md)
29
+ Node/BFF dataspace discovery, hosting-operator resolution, provider
30
+ resolution, and the correct integration boundary for fallback and cache.
31
+ 4. [gdc-sdk-core-ts/docs/SDK_FLOWS_101.md](https://github.com/Global-DataCare/gdc-sdk-core-ts/blob/main/docs/SDK_FLOWS_101.md)
29
32
  Actor split and business-flow map across organization, individual,
30
33
  permissions, invitation, import, and SMART flows.
31
- 4. [gdc-common-utils-ts/src/examples/](https://gitlab.dev.accuro.es/idi/espacio-de-datos/global-datacare/gdc-common-utils-ts/-/tree/main/src/examples)
34
+ 5. [gdc-common-utils-ts/src/examples/](https://github.com/Global-DataCare/gdc-common-utils-ts/tree/main/src/examples)
32
35
  Shared payload values used by the docs and tests.
33
- 5. [gdc-common-utils-ts/docs/LIFECYCLE_101.md](https://gitlab.dev.accuro.es/idi/espacio-de-datos/global-datacare/gdc-common-utils-ts/-/blob/main/docs/LIFECYCLE_101.md)
36
+ 6. [gdc-common-utils-ts/docs/LIFECYCLE_101.md](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/docs/LIFECYCLE_101.md)
34
37
  Canonical `enable/disable/delete` semantics and copy/paste placeholders.
35
38
 
36
39
  If you need the shortest path:
@@ -46,6 +49,8 @@ If you need the shortest path:
46
49
  [`NodeHttpClient`](src/node-runtime-client.ts)
47
50
  - step-by-step runtime usage:
48
51
  [docs/SDK_INTEGRATION_101.md](./docs/SDK_INTEGRATION_101.md)
52
+ - dataspace discovery and fallback/cache boundary:
53
+ [docs/DISCOVERY_101.md](./docs/DISCOVERY_101.md)
49
54
 
50
55
  ## Executable Usage Examples
51
56
 
@@ -65,6 +70,42 @@ Open these tests when you want to see exact method calls and exact inputs:
65
70
  SMART token request flow.
66
71
  - [tests/live-gw-node-runtime.e2e.test.mjs](tests/live-gw-node-runtime.e2e.test.mjs)
67
72
  End-to-end runtime wiring against a real GW environment.
73
+ - [tests/dataspace-resolver.101.test.mjs](tests/dataspace-resolver.101.test.mjs)
74
+ Dataspace discovery 101 with capability filtering, jurisdiction filtering,
75
+ reader-vs-provider semantics, and fetcher-level fallback/cache examples.
76
+
77
+ ## Dataspace Discovery Quick Map
78
+
79
+ Use the Node resolver when your backend or BFF needs to:
80
+
81
+ - start from preloaded hosting-operator semantics
82
+ - fetch the canonical `/.well-known/dspace-version` entrypoint
83
+ - derive the participant-scoped `/dsp/catalog/dcat.json` artifact
84
+ - return normalized provider/operator matches to portal or app backends
85
+
86
+ Primary references:
87
+
88
+ - [docs/DISCOVERY_101.md](./docs/DISCOVERY_101.md)
89
+ - [tests/dataspace-resolver.101.test.mjs](tests/dataspace-resolver.101.test.mjs)
90
+ - [tests/dataspace-resolver.test.mjs](tests/dataspace-resolver.test.mjs)
91
+
92
+ Copy/paste starting point:
93
+
94
+ ```ts
95
+ import { HttpDataspaceResolver } from 'gdc-sdk-node-ts';
96
+ import { ServiceCapabilityToken } from 'gdc-common-utils-ts/constants';
97
+
98
+ const resolver = new HttpDataspaceResolver({
99
+ hostingOperators,
100
+ fetcher, // optional injection for tests, fallback, tracing, or custom agents
101
+ });
102
+
103
+ const providers = await resolver.resolvePublishedProviders({
104
+ sector: 'animal-care',
105
+ jurisdiction: 'ES',
106
+ providerCapability: ServiceCapabilityToken.IndexProvider,
107
+ });
108
+ ```
68
109
 
69
110
  ## Actor Split And Runtime Scope
70
111
 
@@ -247,16 +288,16 @@ Teaching rule:
247
288
  ## Shared Contract Sources
248
289
 
249
290
  - [gdc-sdk-core-ts/README.md](https://github.com/Global-DataCare/gdc-sdk-core-ts/blob/main/README.md)
250
- - [gdc-common-utils-ts/docs/CONSENT_ACCESS_101.md](https://gitlab.dev.accuro.es/idi/espacio-de-datos/global-datacare/gdc-common-utils-ts/-/blob/main/docs/CONSENT_ACCESS_101.md)
291
+ - [gdc-common-utils-ts/docs/CONSENT_ACCESS_101.md](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/docs/CONSENT_ACCESS_101.md)
251
292
 
252
293
  Reusable payload examples:
253
294
 
254
- - [gdc-common-utils-ts/src/examples/organization-controller.ts](https://gitlab.dev.accuro.es/idi/espacio-de-datos/global-datacare/gdc-common-utils-ts/-/blob/main/src/examples/organization-controller.ts)
255
- - [gdc-common-utils-ts/src/examples/individual-controller.ts](https://gitlab.dev.accuro.es/idi/espacio-de-datos/global-datacare/gdc-common-utils-ts/-/blob/main/src/examples/individual-controller.ts)
256
- - [gdc-common-utils-ts/src/examples/professional.ts](https://gitlab.dev.accuro.es/idi/espacio-de-datos/global-datacare/gdc-common-utils-ts/-/blob/main/src/examples/professional.ts)
257
- - [gdc-common-utils-ts/src/examples/shared.ts](https://gitlab.dev.accuro.es/idi/espacio-de-datos/global-datacare/gdc-common-utils-ts/-/blob/main/src/examples/shared.ts)
258
- - [gdc-common-utils-ts/src/examples/lifecycle.ts](https://gitlab.dev.accuro.es/idi/espacio-de-datos/global-datacare/gdc-common-utils-ts/-/blob/main/src/examples/lifecycle.ts)
259
- - [gdc-common-utils-ts/src/examples/api-flow-examples.ts](https://gitlab.dev.accuro.es/idi/espacio-de-datos/global-datacare/gdc-common-utils-ts/-/blob/main/src/examples/api-flow-examples.ts)
295
+ - [gdc-common-utils-ts/src/examples/organization-controller.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/organization-controller.ts)
296
+ - [gdc-common-utils-ts/src/examples/individual-controller.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/individual-controller.ts)
297
+ - [gdc-common-utils-ts/src/examples/professional.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/professional.ts)
298
+ - [gdc-common-utils-ts/src/examples/shared.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/shared.ts)
299
+ - [gdc-common-utils-ts/src/examples/lifecycle.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/lifecycle.ts)
300
+ - [gdc-common-utils-ts/src/examples/api-flow-examples.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/api-flow-examples.ts)
260
301
 
261
302
  ## API Index
262
303
 
@@ -0,0 +1,24 @@
1
+ import type { HostingOperatorMatch, PublishedProviderMatch, ResolveHostingOperatorsInput, ResolvePublishedProvidersInput } from './types.js';
2
+ /**
3
+ * Backend/BFF-facing abstraction for dataspace discovery resolution.
4
+ *
5
+ * Responsibility boundary:
6
+ * - node runtimes orchestrate VC parsing, host-catalog fetch, and capability
7
+ * filtering
8
+ * - semantic extraction and EU coverage inference come from
9
+ * `gdc-common-utils-ts`
10
+ * - tenant-host linkage is resolved from host public catalogs, not from private
11
+ * tenant VC fields
12
+ */
13
+ export declare abstract class DataspaceResolver {
14
+ /**
15
+ * Resolves hosting operators whose semantic service metadata matches the
16
+ * requested sector, capability set, and optional coverage dimensions.
17
+ */
18
+ abstract resolveHostingOperators(input: ResolveHostingOperatorsInput): Promise<HostingOperatorMatch[]>;
19
+ /**
20
+ * Resolves publicly published provider offerings starting from eligible
21
+ * hosting operators and their public discovery catalogs.
22
+ */
23
+ abstract resolvePublishedProviders(input: ResolvePublishedProvidersInput): Promise<PublishedProviderMatch[]>;
24
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ /**
3
+ * Backend/BFF-facing abstraction for dataspace discovery resolution.
4
+ *
5
+ * Responsibility boundary:
6
+ * - node runtimes orchestrate VC parsing, host-catalog fetch, and capability
7
+ * filtering
8
+ * - semantic extraction and EU coverage inference come from
9
+ * `gdc-common-utils-ts`
10
+ * - tenant-host linkage is resolved from host public catalogs, not from private
11
+ * tenant VC fields
12
+ */
13
+ export class DataspaceResolver {
14
+ }
@@ -0,0 +1,30 @@
1
+ import { DataspaceResolver } from './DataspaceResolver.js';
2
+ import type { HostingOperatorMatch, HttpDataspaceResolverOptions, PublishedProviderMatch, ResolveHostingOperatorsInput, ResolvePublishedProvidersInput } from './types.js';
3
+ /**
4
+ * Concrete node/BFF resolver that combines preloaded hosting-operator records
5
+ * with each host's public service-autodiscovery catalog.
6
+ */
7
+ export declare class HttpDataspaceResolver extends DataspaceResolver {
8
+ private readonly hostingOperators;
9
+ private readonly fetcher;
10
+ constructor(options: HttpDataspaceResolverOptions);
11
+ /**
12
+ * Resolves hosting operators from the preloaded semantic records.
13
+ */
14
+ resolveHostingOperators(input: ResolveHostingOperatorsInput): Promise<HostingOperatorMatch[]>;
15
+ /**
16
+ * Resolves published providers by fetching and filtering eligible host public
17
+ * discovery catalogs.
18
+ */
19
+ resolvePublishedProviders(input: ResolvePublishedProvidersInput): Promise<PublishedProviderMatch[]>;
20
+ /**
21
+ * Fetches a public discovery catalog for a hosting operator.
22
+ *
23
+ * Resolution order:
24
+ * - canonical DSP entrypoint via `discoveryUrl` (`/.well-known/dspace-version`)
25
+ * - direct catalog artifact compatibility via `catalogUrl`
26
+ */
27
+ private fetchCatalog;
28
+ private resolveCatalogArtifactUrl;
29
+ private fetchDspaceVersionMetadata;
30
+ }
@@ -0,0 +1,231 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ import { DataspaceCoverageScope, DataspaceProtocolVersions, DataspaceWellKnownPaths, deriveGwCatalogArtifactUrlFromDspaceVersion, inferCoverageScopeFromCountryCode, isProviderServiceCapability, normalizeCountryCode, } from 'gdc-common-utils-ts';
3
+ import { DataspaceResolver } from './DataspaceResolver.js';
4
+ function isObject(value) {
5
+ return !!value && typeof value === 'object' && !Array.isArray(value);
6
+ }
7
+ function asString(value) {
8
+ return typeof value === 'string' ? value.trim() : '';
9
+ }
10
+ function equalsIgnoreCase(left, right) {
11
+ return left.trim().toLowerCase() === right.trim().toLowerCase();
12
+ }
13
+ function splitTokens(value) {
14
+ if (Array.isArray(value)) {
15
+ return value.map((entry) => asString(entry)).filter(Boolean);
16
+ }
17
+ const raw = typeof value === 'string' ? value : '';
18
+ if (!raw)
19
+ return [];
20
+ return raw.split(',').map((entry) => entry.trim()).filter(Boolean);
21
+ }
22
+ function hasAllTokens(values, requiredValues) {
23
+ if (!requiredValues?.length)
24
+ return true;
25
+ const normalizedValues = new Set((values || []).map((value) => value.trim().toLowerCase()).filter(Boolean));
26
+ return requiredValues.every((requiredValue) => normalizedValues.has(requiredValue.trim().toLowerCase()));
27
+ }
28
+ function matchSectorValues(values, sector) {
29
+ if (!sector)
30
+ return true;
31
+ return (values || []).some((value) => equalsIgnoreCase(value, sector));
32
+ }
33
+ function buildCoverageTokens(recordCountry, areaServed, coverageScope) {
34
+ const tokens = new Set();
35
+ splitTokens(areaServed).forEach((token) => tokens.add(token.toUpperCase()));
36
+ const normalizedCountry = normalizeCountryCode(recordCountry);
37
+ if (normalizedCountry) {
38
+ tokens.add(normalizedCountry);
39
+ const inferred = inferCoverageScopeFromCountryCode(normalizedCountry);
40
+ if (inferred)
41
+ tokens.add(inferred.toUpperCase());
42
+ }
43
+ if (coverageScope) {
44
+ tokens.add(coverageScope.trim().toUpperCase());
45
+ }
46
+ return tokens;
47
+ }
48
+ function matchesDiscoveryCoverage(recordCountry, areaServed, filterJurisdiction, filterCoverageScope) {
49
+ const normalizedJurisdiction = normalizeCountryCode(filterJurisdiction);
50
+ const normalizedCoverageScope = asString(filterCoverageScope).toUpperCase();
51
+ if (!normalizedJurisdiction && !normalizedCoverageScope)
52
+ return true;
53
+ const tokens = buildCoverageTokens(recordCountry, areaServed, undefined);
54
+ if (normalizedJurisdiction && tokens.has(normalizedJurisdiction))
55
+ return true;
56
+ if (normalizedCoverageScope && tokens.has(normalizedCoverageScope))
57
+ return true;
58
+ if (normalizedJurisdiction) {
59
+ const inferred = inferCoverageScopeFromCountryCode(normalizedJurisdiction);
60
+ if (inferred && tokens.has(inferred.toUpperCase()))
61
+ return true;
62
+ }
63
+ if (normalizedCoverageScope === DataspaceCoverageScope.EuropeanUnion) {
64
+ if (Array.from(tokens).some((token) => inferCoverageScopeFromCountryCode(token) === DataspaceCoverageScope.EuropeanUnion)) {
65
+ return true;
66
+ }
67
+ }
68
+ return false;
69
+ }
70
+ function toHostingOperatorCatalog(value) {
71
+ if (!isObject(value))
72
+ return null;
73
+ const providers = value.providers;
74
+ if (!Array.isArray(providers))
75
+ return null;
76
+ const normalizedProviders = providers
77
+ .filter(isObject)
78
+ .map((provider) => {
79
+ const normalized = {
80
+ providerDid: asString(provider.providerDid),
81
+ serviceType: asString(provider.serviceType),
82
+ category: asString(provider.category),
83
+ areaServed: asString(provider.areaServed) || undefined,
84
+ endpointUrl: asString(provider.endpointUrl) || undefined,
85
+ discoveryUrl: asString(provider.discoveryUrl) || undefined,
86
+ catalogUrl: asString(provider.catalogUrl) || undefined,
87
+ };
88
+ return normalized.providerDid && normalized.serviceType && normalized.category
89
+ ? normalized
90
+ : null;
91
+ })
92
+ .filter((provider) => Boolean(provider));
93
+ return {
94
+ hostingOperatorDid: asString(value.hostingOperatorDid) || undefined,
95
+ discoveryUrl: asString(value.discoveryUrl) || undefined,
96
+ catalogUrl: asString(value.catalogUrl) || undefined,
97
+ providers: normalizedProviders,
98
+ };
99
+ }
100
+ function matchHostingOperatorRecord(record, input) {
101
+ if (!matchSectorValues(record.categories, input.sector))
102
+ return false;
103
+ if (!hasAllTokens(record.serviceTypes, input.requiredCapabilities))
104
+ return false;
105
+ if (!matchesDiscoveryCoverage(record.addressCountry, record.areaServed, input.jurisdiction, input.coverageScope))
106
+ return false;
107
+ return true;
108
+ }
109
+ function matchPublishedProviderRecord(provider, input) {
110
+ if (!equalsIgnoreCase(provider.category, input.sector))
111
+ return false;
112
+ if (!isProviderServiceCapability(provider.serviceType))
113
+ return false;
114
+ if (!equalsIgnoreCase(provider.serviceType, input.providerCapability))
115
+ return false;
116
+ if (!matchesDiscoveryCoverage(undefined, provider.areaServed, input.jurisdiction, input.coverageScope))
117
+ return false;
118
+ return true;
119
+ }
120
+ /**
121
+ * Concrete node/BFF resolver that combines preloaded hosting-operator records
122
+ * with each host's public service-autodiscovery catalog.
123
+ */
124
+ export class HttpDataspaceResolver extends DataspaceResolver {
125
+ constructor(options) {
126
+ super();
127
+ this.hostingOperators = [...options.hostingOperators];
128
+ this.fetcher = options.fetcher || globalThis.fetch.bind(globalThis);
129
+ }
130
+ /**
131
+ * Resolves hosting operators from the preloaded semantic records.
132
+ */
133
+ async resolveHostingOperators(input) {
134
+ return this.hostingOperators
135
+ .filter(({ record }) => matchHostingOperatorRecord(record, input))
136
+ .map(({ operatorDid, discoveryUrl, record, catalogUrl }) => ({
137
+ operatorDid,
138
+ record,
139
+ matchedCapabilities: (input.requiredCapabilities || []).filter((capability) => record.serviceTypes.some((value) => equalsIgnoreCase(value, capability))),
140
+ discoveryUrl,
141
+ catalogUrl,
142
+ }))
143
+ .sort((left, right) => left.operatorDid.localeCompare(right.operatorDid));
144
+ }
145
+ /**
146
+ * Resolves published providers by fetching and filtering eligible host public
147
+ * discovery catalogs.
148
+ */
149
+ async resolvePublishedProviders(input) {
150
+ const eligibleHosts = await this.resolveHostingOperators({
151
+ sector: input.sector,
152
+ jurisdiction: input.jurisdiction,
153
+ coverageScope: input.coverageScope,
154
+ requiredCapabilities: [input.providerCapability],
155
+ });
156
+ const catalogs = await Promise.all(eligibleHosts.map(async (host) => ({
157
+ host,
158
+ catalog: await this.fetchCatalog(host.discoveryUrl, host.catalogUrl),
159
+ })));
160
+ return catalogs.flatMap(({ host, catalog }) => {
161
+ if (!catalog)
162
+ return [];
163
+ return catalog.providers
164
+ .filter((record) => matchPublishedProviderRecord(record, input))
165
+ .map((record) => ({
166
+ providerDid: record.providerDid,
167
+ record,
168
+ hostingOperator: host.record,
169
+ hostingOperatorDid: host.operatorDid,
170
+ discoveryUrl: record.discoveryUrl || catalog.discoveryUrl || host.discoveryUrl,
171
+ catalogUrl: record.catalogUrl || catalog.catalogUrl || host.catalogUrl,
172
+ }));
173
+ }).sort((left, right) => left.providerDid.localeCompare(right.providerDid));
174
+ }
175
+ /**
176
+ * Fetches a public discovery catalog for a hosting operator.
177
+ *
178
+ * Resolution order:
179
+ * - canonical DSP entrypoint via `discoveryUrl` (`/.well-known/dspace-version`)
180
+ * - direct catalog artifact compatibility via `catalogUrl`
181
+ */
182
+ async fetchCatalog(discoveryUrl, catalogUrl) {
183
+ const resolvedCatalogUrl = await this.resolveCatalogArtifactUrl(discoveryUrl, catalogUrl);
184
+ if (!resolvedCatalogUrl)
185
+ return undefined;
186
+ const response = await this.fetcher(resolvedCatalogUrl, {
187
+ method: 'GET',
188
+ headers: {
189
+ accept: 'application/json',
190
+ },
191
+ });
192
+ if (!response.ok) {
193
+ return undefined;
194
+ }
195
+ const body = await response.json();
196
+ return toHostingOperatorCatalog(body) || undefined;
197
+ }
198
+ async resolveCatalogArtifactUrl(discoveryUrl, catalogUrl) {
199
+ const normalizedDiscoveryUrl = String(discoveryUrl || '').trim();
200
+ if (normalizedDiscoveryUrl) {
201
+ if (normalizedDiscoveryUrl.endsWith(DataspaceWellKnownPaths.VersionMetadata)) {
202
+ const metadata = await this.fetchDspaceVersionMetadata(normalizedDiscoveryUrl);
203
+ return deriveGwCatalogArtifactUrlFromDspaceVersion(normalizedDiscoveryUrl, metadata, DataspaceProtocolVersions.Current);
204
+ }
205
+ return normalizedDiscoveryUrl;
206
+ }
207
+ const normalizedCatalogUrl = String(catalogUrl || '').trim();
208
+ return normalizedCatalogUrl || undefined;
209
+ }
210
+ async fetchDspaceVersionMetadata(discoveryUrl) {
211
+ const response = await this.fetcher(discoveryUrl, {
212
+ method: 'GET',
213
+ headers: {
214
+ accept: 'application/json',
215
+ },
216
+ });
217
+ if (!response.ok) {
218
+ return undefined;
219
+ }
220
+ const body = await response.json();
221
+ return isDspaceVersionMetadata(body)
222
+ ? body
223
+ : undefined;
224
+ }
225
+ }
226
+ function isDspaceVersionMetadata(value) {
227
+ if (!isObject(value))
228
+ return false;
229
+ return Array.isArray(value.protocolVersions)
230
+ && value.protocolVersions.every((entry) => isObject(entry) && asString(entry.version) && asString(entry.path));
231
+ }
@@ -0,0 +1,3 @@
1
+ export * from './DataspaceResolver.js';
2
+ export * from './HttpDataspaceResolver.js';
3
+ export * from './types.js';
@@ -0,0 +1,4 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ export * from './DataspaceResolver.js';
3
+ export * from './HttpDataspaceResolver.js';
4
+ export * from './types.js';
@@ -0,0 +1,68 @@
1
+ import type { DataspaceDiscoveryFilter, HostingOperatorDiscoveryCatalog, HostingOperatorSemanticRecord, PublishedProviderCatalogRecord, TenantServiceSemanticRecord } from 'gdc-common-utils-ts';
2
+ /**
3
+ * Input for resolving hosting operators that can serve a given dataspace use
4
+ * case.
5
+ */
6
+ export type ResolveHostingOperatorsInput = Omit<DataspaceDiscoveryFilter, 'capability'> & Readonly<{
7
+ requiredCapabilities: readonly string[];
8
+ }>;
9
+ /**
10
+ * Input for resolving publicly published providers through hosting-operator
11
+ * catalogs.
12
+ */
13
+ export type ResolvePublishedProvidersInput = Omit<DataspaceDiscoveryFilter, 'capability' | 'requiredCapabilities'> & Readonly<{
14
+ providerCapability: string;
15
+ }>;
16
+ /**
17
+ * Preloaded semantic hosting-operator record used by the node runtime to
18
+ * resolve public discovery data without re-parsing VCs.
19
+ */
20
+ export type PreloadedHostingOperatorRecord = Readonly<{
21
+ operatorDid: string;
22
+ /**
23
+ * Canonical DSP discovery entrypoint.
24
+ *
25
+ * For GW CORE this should normally be `/.well-known/dspace-version`.
26
+ */
27
+ discoveryUrl?: string;
28
+ /**
29
+ * @deprecated Prefer `discoveryUrl`. This remains as a compatibility field
30
+ * for direct catalog artifact URLs.
31
+ */
32
+ catalogUrl?: string;
33
+ record: HostingOperatorSemanticRecord;
34
+ }>;
35
+ /**
36
+ * Runtime configuration for the HTTP-capable dataspace resolver.
37
+ */
38
+ export type HttpDataspaceResolverOptions = Readonly<{
39
+ hostingOperators: readonly PreloadedHostingOperatorRecord[];
40
+ fetcher?: typeof fetch;
41
+ requestHeaders?: Record<string, string>;
42
+ }>;
43
+ /**
44
+ * Normalized hosting-operator match returned by a node/BFF resolver.
45
+ */
46
+ export type HostingOperatorMatch = Readonly<{
47
+ operatorDid: string;
48
+ record: HostingOperatorSemanticRecord;
49
+ matchedCapabilities: string[];
50
+ discoveryUrl?: string;
51
+ catalogUrl?: string;
52
+ }>;
53
+ /**
54
+ * Normalized published-provider match returned by a node/BFF resolver.
55
+ */
56
+ export type PublishedProviderMatch = Readonly<{
57
+ providerDid: string;
58
+ record: PublishedProviderCatalogRecord;
59
+ hostingOperator: HostingOperatorSemanticRecord;
60
+ hostingOperatorDid: string;
61
+ discoveryUrl?: string;
62
+ catalogUrl?: string;
63
+ tenantSemanticRecord?: TenantServiceSemanticRecord;
64
+ }>;
65
+ /**
66
+ * Shared host catalog shape returned by HTTP discovery endpoints.
67
+ */
68
+ export type HostingOperatorCatalog = HostingOperatorDiscoveryCatalog;
@@ -0,0 +1,2 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ export {};
@@ -3,11 +3,13 @@ import type { PollOptions, SubmitAndPollResult } from './orchestration/client-po
3
3
  * Current host-registry route context for existing host endpoints.
4
4
  *
5
5
  * This is a routing object for host registry calls. It is not the same thing as
6
- * a node-operator discovery descriptor.
6
+ * a host discovery descriptor.
7
7
  */
8
8
  export type HostRouteContext = {
9
9
  jurisdiction: string;
10
- sector: string;
10
+ hostNetwork?: string;
11
+ /** @deprecated Use `hostNetwork`. */
12
+ sector?: string;
11
13
  controllerDid?: string;
12
14
  hostDid?: string;
13
15
  };
@@ -17,6 +19,8 @@ export type HostRouteContext = {
17
19
  export type LegalOrganizationOrderInput = {
18
20
  offerId: string;
19
21
  jurisdiction?: string;
22
+ hostNetwork?: string;
23
+ /** @deprecated Use `hostNetwork`. */
20
24
  sector?: string;
21
25
  dataType?: string;
22
26
  additionalClaims?: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export * from './smart-token.js';
11
11
  export * from './resource-operations.js';
12
12
  export * from './session.js';
13
13
  export * from './node-runtime-client.js';
14
+ export * from './discovery/index.js';
14
15
  export * from './gdc-session-bridge.js';
15
16
  export * from './orchestration/client-port.js';
16
17
  export * from './orchestration/host-onboarding-sdk.js';
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ export * from './smart-token.js';
12
12
  export * from './resource-operations.js';
13
13
  export * from './session.js';
14
14
  export * from './node-runtime-client.js';
15
+ export * from './discovery/index.js';
15
16
  export * from './gdc-session-bridge.js';
16
17
  export * from './orchestration/client-port.js';
17
18
  export * from './orchestration/host-onboarding-sdk.js';
@@ -12,6 +12,21 @@ import type { NodeRuntimeClient, PollOptions, PollResult, SubmitAndPollResult, S
12
12
  export type HttpRuntimeClientOptions = {
13
13
  baseUrl: string;
14
14
  bearerToken?: string;
15
+ /**
16
+ * Optional ICA-issued runtime/software proof token reused as the default
17
+ * HTTP Bearer credential in demo/compat profiles when no explicit
18
+ * `bearerToken` is provided.
19
+ *
20
+ * This keeps the SDK wiring ready for a future ICA-authorized software
21
+ * runtime contract without forcing callers to overload the semantic name
22
+ * `bearerToken` in documentation or app code.
23
+ *
24
+ * Current `gwtemplate-node-ts` demo/bootstrap flows do not yet require a
25
+ * registered software/runtime proof for this path, so callers may omit this
26
+ * field or pass an empty string until the ICA runtime-registration contract
27
+ * is finalized.
28
+ */
29
+ runtimeVpToken?: string;
15
30
  /**
16
31
  * Host app identity required by GW CORE.
17
32
  *
@@ -44,6 +59,7 @@ export type NodeHttpClientOptions = HttpRuntimeClientOptions;
44
59
  export declare class HttpRuntimeClient implements NodeRuntimeClient {
45
60
  private readonly baseUrl;
46
61
  private readonly bearerToken?;
62
+ private readonly runtimeVpToken?;
47
63
  private readonly resolvedAppInfo?;
48
64
  private readonly ctx?;
49
65
  private readonly defaultHeaders;
@@ -54,6 +70,7 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
54
70
  * @param options.baseUrl Gateway base URL without trailing slash.
55
71
  * @param options.interopMode Optional runtime interoperability mode from the SDK config layer (`demo`, `compat`, `strict`).
56
72
  * @param options.bearerToken Optional bearer token reused for direct HTTP calls.
73
+ * @param options.runtimeVpToken Optional ICA-issued runtime/software proof token reused as Bearer when `bearerToken` is not set. Current `gwtemplate-node-ts` demo/bootstrap flows may omit it or pass an empty string because software/runtime registration is not enforced there yet.
57
74
  * @param options.appInfo Optional GW CORE app identity. When present, the
58
75
  * client injects `AppId` and `AppVersion` into all outgoing requests.
59
76
  * @param options.ctx Optional default route context.
@@ -69,6 +86,10 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
69
86
  * Returns the standard GW CORE headers currently injected by the Node client.
70
87
  */
71
88
  getAppHeaders(): Record<'AppId' | 'AppVersion', string> | undefined;
89
+ /**
90
+ * Returns the configured ICA-issued runtime/software proof token, when present.
91
+ */
92
+ getRuntimeVpToken(): string | undefined;
72
93
  /**
73
94
  * Builds a canonical GDC v1 resource/action path from a route context.
74
95
  */
@@ -26,6 +26,7 @@ export class HttpRuntimeClient {
26
26
  * @param options.baseUrl Gateway base URL without trailing slash.
27
27
  * @param options.interopMode Optional runtime interoperability mode from the SDK config layer (`demo`, `compat`, `strict`).
28
28
  * @param options.bearerToken Optional bearer token reused for direct HTTP calls.
29
+ * @param options.runtimeVpToken Optional ICA-issued runtime/software proof token reused as Bearer when `bearerToken` is not set. Current `gwtemplate-node-ts` demo/bootstrap flows may omit it or pass an empty string because software/runtime registration is not enforced there yet.
29
30
  * @param options.appInfo Optional GW CORE app identity. When present, the
30
31
  * client injects `AppId` and `AppVersion` into all outgoing requests.
31
32
  * @param options.ctx Optional default route context.
@@ -35,7 +36,10 @@ export class HttpRuntimeClient {
35
36
  constructor(options) {
36
37
  this.tokenCache = new Map();
37
38
  this.baseUrl = String(options.baseUrl || '').replace(/\/+$/, '');
38
- this.bearerToken = String(options.bearerToken || '').trim() || undefined;
39
+ this.runtimeVpToken = String(options.runtimeVpToken || '').trim() || undefined;
40
+ this.bearerToken = String(options.bearerToken || '').trim()
41
+ || this.runtimeVpToken
42
+ || undefined;
39
43
  this.resolvedAppInfo = options.appInfo ? resolveAppInfo(options.appInfo) : undefined;
40
44
  this.ctx = options.ctx;
41
45
  this.defaultHeaders = {
@@ -59,6 +63,12 @@ export class HttpRuntimeClient {
59
63
  return undefined;
60
64
  return buildAppHeaders(this.resolvedAppInfo);
61
65
  }
66
+ /**
67
+ * Returns the configured ICA-issued runtime/software proof token, when present.
68
+ */
69
+ getRuntimeVpToken() {
70
+ return this.runtimeVpToken;
71
+ }
62
72
  /**
63
73
  * Builds a canonical GDC v1 resource/action path from a route context.
64
74
  */
@@ -503,14 +513,15 @@ export class HttpRuntimeClient {
503
513
  }
504
514
  hostRegistryPath(ctx, resourceType, action) {
505
515
  const hostCtx = this.requireHostRouteContext(ctx);
506
- return `/host/cds-${encodeURIComponent(hostCtx.jurisdiction)}/v1/${encodeURIComponent(hostCtx.sector)}/registry/org.schema/${encodeURIComponent(resourceType)}/${encodeURIComponent(action)}`;
516
+ return `/host/cds-${encodeURIComponent(hostCtx.jurisdiction)}/v1/${encodeURIComponent(hostCtx.hostNetwork || '')}/registry/org.schema/${encodeURIComponent(resourceType)}/${encodeURIComponent(action)}`;
507
517
  }
508
518
  requireHostRouteContext(ctx) {
519
+ const runtimeCtx = (this.ctx || {});
509
520
  const jurisdiction = String(ctx?.jurisdiction || this.ctx?.jurisdiction || '').trim();
510
- const sector = String(ctx?.sector || this.ctx?.sector || '').trim();
511
- if (!jurisdiction || !sector)
521
+ const hostNetwork = String(ctx?.hostNetwork || ctx?.sector || runtimeCtx.hostNetwork || runtimeCtx.sector || '').trim();
522
+ if (!jurisdiction || !hostNetwork)
512
523
  throw new Error('Host route context is required.');
513
- return { jurisdiction, sector };
524
+ return { jurisdiction, hostNetwork };
514
525
  }
515
526
  hostRegistryOrganizationActivatePath(ctx) { return this.hostRegistryPath(ctx, 'Organization', '_activate'); }
516
527
  hostRegistryOrganizationActivatePollPath(ctx) { return this.hostRegistryPath(ctx, 'Organization', '_activate-response'); }
@@ -1,5 +1,6 @@
1
1
  import type { NodeOperatorNetworkType } from 'gdc-common-utils-ts/constants/network';
2
2
  import type { DataPersistencePolicy } from 'gdc-sdk-core-ts';
3
+ type HostNetworkType = NodeOperatorNetworkType;
3
4
  export type LegacyNodeSourcePackage = never;
4
5
  /**
5
6
  * Deployment/runtime form factor of the Node SDK host process itself.
@@ -28,17 +29,19 @@ export type TenantContext = {
28
29
  sector: string;
29
30
  };
30
31
  /**
31
- * Discovery/bootstrap context for a node operator environment.
32
+ * Discovery/bootstrap context for a host environment.
32
33
  *
33
- * This describes the operator network/environment itself. It does not replace
34
+ * This describes the host network/environment itself. It does not replace
34
35
  * the tenant route context used by tenant-scoped GW endpoints.
35
36
  */
36
- export type NodeOperatorContext = {
37
- networkType: NodeOperatorNetworkType;
37
+ export type HostContext = {
38
+ networkType: HostNetworkType;
38
39
  jurisdiction: string;
39
40
  operatorDid?: string;
40
41
  baseUrl?: string;
41
42
  };
43
+ /** @deprecated Use `HostContext`. */
44
+ export type NodeOperatorContext = HostContext;
42
45
  export type NodeFetchLike = typeof fetch;
43
46
  /**
44
47
  * Node runtime configuration for backend/BFF integrations.
@@ -63,3 +66,4 @@ export type NodePackageStatus = {
63
66
  status: 'bootstrap';
64
67
  };
65
68
  export declare const GDC_SDK_NODE_STATUS: NodePackageStatus;
69
+ export {};
@@ -0,0 +1,51 @@
1
+ import type { NodeSdkCommunicationIdentityBootstrapResult } from './identity-bootstrap.js';
2
+ export type SoftwareRuntimeCredentialInput = {
3
+ /**
4
+ * Public DID of the portal/backend/software runtime profile.
5
+ */
6
+ softwareDid: string;
7
+ /**
8
+ * Technical communication identity previously created with
9
+ * `initializeCommunicationIdentity(...)`.
10
+ */
11
+ deviceIdentity: NodeSdkCommunicationIdentityBootstrapResult;
12
+ /**
13
+ * Optional VC identifier.
14
+ */
15
+ credentialId?: string;
16
+ /**
17
+ * Optional ICA issuer DID placeholder for examples/tests.
18
+ */
19
+ issuerDid?: string;
20
+ /**
21
+ * Optional human-readable software/application name.
22
+ */
23
+ softwareName?: string;
24
+ /**
25
+ * Optional issuance timestamp.
26
+ */
27
+ issuanceDate?: string;
28
+ /**
29
+ * Optional extra `credentialSubject` claims for profile-specific examples.
30
+ */
31
+ additionalCredentialSubject?: Record<string, unknown>;
32
+ };
33
+ export type SoftwareRuntimeCredential = {
34
+ '@context': string[];
35
+ type: string[];
36
+ id?: string;
37
+ issuer?: string;
38
+ issuanceDate?: string;
39
+ credentialSubject: Record<string, unknown>;
40
+ };
41
+ /**
42
+ * Builds a copy/paste-friendly VC example for an ICA-authorized software or
43
+ * runtime profile. The key binding is taken from the technical communication
44
+ * signing key generated by `initializeCommunicationIdentity(...)`.
45
+ *
46
+ * This helper is intentionally example-oriented. It wires the runtime
47
+ * communication `kid` into `credentialSubject.material` so SDK docs and tests
48
+ * can express the intended future ICA contract without hand-shaping the object
49
+ * inline every time.
50
+ */
51
+ export declare function buildSoftwareRuntimeCredential(input: SoftwareRuntimeCredentialInput): SoftwareRuntimeCredential;
@@ -0,0 +1,34 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ /**
3
+ * Builds a copy/paste-friendly VC example for an ICA-authorized software or
4
+ * runtime profile. The key binding is taken from the technical communication
5
+ * signing key generated by `initializeCommunicationIdentity(...)`.
6
+ *
7
+ * This helper is intentionally example-oriented. It wires the runtime
8
+ * communication `kid` into `credentialSubject.material` so SDK docs and tests
9
+ * can express the intended future ICA contract without hand-shaping the object
10
+ * inline every time.
11
+ */
12
+ export function buildSoftwareRuntimeCredential(input) {
13
+ const communicationKid = String(input.deviceIdentity.commSigningKeyPair.publicJWKey.kid || '').trim();
14
+ if (!communicationKid) {
15
+ throw new Error('buildSoftwareRuntimeCredential requires deviceIdentity.commSigningKeyPair.publicJWKey.kid.');
16
+ }
17
+ const softwareDid = String(input.softwareDid || '').trim();
18
+ if (!softwareDid) {
19
+ throw new Error('buildSoftwareRuntimeCredential requires softwareDid.');
20
+ }
21
+ return {
22
+ '@context': ['https://www.w3.org/2018/credentials/v1', 'https://schema.org'],
23
+ type: ['VerifiableCredential', 'SoftwareApplicationCredential'],
24
+ ...(input.credentialId ? { id: input.credentialId } : {}),
25
+ ...(input.issuerDid ? { issuer: input.issuerDid } : {}),
26
+ ...(input.issuanceDate ? { issuanceDate: input.issuanceDate } : {}),
27
+ credentialSubject: {
28
+ id: softwareDid,
29
+ material: communicationKid,
30
+ ...(input.softwareName ? { name: input.softwareName } : {}),
31
+ ...(input.additionalCredentialSubject || {}),
32
+ },
33
+ };
34
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-sdk-node-ts",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Next-generation Node runtime package for the GDC SDK family",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Antifraud Services Inc.",
@@ -17,8 +17,8 @@
17
17
  "test:e2e:live-gw": "npm run build && RUN_LIVE_GW_E2E=1 node --test tests/live-gw-node-runtime.e2e.test.mjs"
18
18
  },
19
19
  "dependencies": {
20
- "gdc-common-utils-ts": "^1.9.0",
21
- "gdc-sdk-core-ts": "^0.5.0"
20
+ "gdc-common-utils-ts": "^1.13.0",
21
+ "gdc-sdk-core-ts": "^0.6.0"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^20.14.10",