gdc-common-utils-ts 1.11.0 → 1.12.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
@@ -86,6 +86,9 @@ import { JweObject, JwtCompactParts } from 'gdc-common-utils-ts/models';
86
86
 
87
87
  ## Cross-Repo Task Docs
88
88
 
89
+ - [docs/DATASPACE_DISCOVERY_ROADMAP.md](docs/DATASPACE_DISCOVERY_ROADMAP.md)
90
+ - cross-repo contract for dataspace discovery semantics, EU coverage
91
+ inference, shared DTOs, and parameterized examples
89
92
  - [docs/consent-access-matrix-task.md](docs/consent-access-matrix-task.md)
90
93
  - next-step design/task document for active consent aggregation, explicit deny precedence, controller views, permission-request communications, and SMART access evaluation
91
94
 
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ISO 3166-1 alpha-2 country codes that currently belong to the European Union.
3
+ *
4
+ * This list is intentionally kept in a runtime-neutral shared package because
5
+ * dataspace discovery may need to infer a broader coverage scope such as `EU`
6
+ * from the semantic country carried in a VC `credentialSubject`.
7
+ */
8
+ export declare const EU_COUNTRY_CODES: readonly ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE"];
9
+ export type EuCountryCode = typeof EU_COUNTRY_CODES[number];
10
+ /**
11
+ * Normalizes a country code into canonical uppercase ISO-2 form.
12
+ *
13
+ * @param countryCode Country code from `credentialSubject.address.addressCountry`
14
+ * or the flattened operational projection.
15
+ * @returns Uppercase ISO-2 form or an empty string when the input is blank.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * normalizeCountryCode('es');
20
+ * // 'ES'
21
+ * ```
22
+ */
23
+ export declare function normalizeCountryCode(countryCode: string | undefined | null): string;
24
+ /**
25
+ * Checks whether the supplied country code belongs to the current EU member set.
26
+ *
27
+ * @param countryCode ISO-2 country code to evaluate.
28
+ * @returns `true` when the normalized code belongs to `EU_COUNTRY_CODES`.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * isEuCountryCode('ES');
33
+ * // true
34
+ * ```
35
+ */
36
+ export declare function isEuCountryCode(countryCode: string | undefined | null): boolean;
@@ -0,0 +1,69 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ /**
3
+ * ISO 3166-1 alpha-2 country codes that currently belong to the European Union.
4
+ *
5
+ * This list is intentionally kept in a runtime-neutral shared package because
6
+ * dataspace discovery may need to infer a broader coverage scope such as `EU`
7
+ * from the semantic country carried in a VC `credentialSubject`.
8
+ */
9
+ export const EU_COUNTRY_CODES = Object.freeze([
10
+ 'AT',
11
+ 'BE',
12
+ 'BG',
13
+ 'HR',
14
+ 'CY',
15
+ 'CZ',
16
+ 'DK',
17
+ 'EE',
18
+ 'FI',
19
+ 'FR',
20
+ 'DE',
21
+ 'GR',
22
+ 'HU',
23
+ 'IE',
24
+ 'IT',
25
+ 'LV',
26
+ 'LT',
27
+ 'LU',
28
+ 'MT',
29
+ 'NL',
30
+ 'PL',
31
+ 'PT',
32
+ 'RO',
33
+ 'SK',
34
+ 'SI',
35
+ 'ES',
36
+ 'SE',
37
+ ]);
38
+ /**
39
+ * Normalizes a country code into canonical uppercase ISO-2 form.
40
+ *
41
+ * @param countryCode Country code from `credentialSubject.address.addressCountry`
42
+ * or the flattened operational projection.
43
+ * @returns Uppercase ISO-2 form or an empty string when the input is blank.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * normalizeCountryCode('es');
48
+ * // 'ES'
49
+ * ```
50
+ */
51
+ export function normalizeCountryCode(countryCode) {
52
+ return String(countryCode || '').trim().toUpperCase();
53
+ }
54
+ /**
55
+ * Checks whether the supplied country code belongs to the current EU member set.
56
+ *
57
+ * @param countryCode ISO-2 country code to evaluate.
58
+ * @returns `true` when the normalized code belongs to `EU_COUNTRY_CODES`.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * isEuCountryCode('ES');
63
+ * // true
64
+ * ```
65
+ */
66
+ export function isEuCountryCode(countryCode) {
67
+ const normalized = normalizeCountryCode(countryCode);
68
+ return normalized ? EU_COUNTRY_CODES.includes(normalized) : false;
69
+ }
@@ -3,6 +3,7 @@ export * from './communication';
3
3
  export * from './cryptography';
4
4
  export * from './device';
5
5
  export * from './did-services';
6
+ export * from './eu-countries';
6
7
  export * from './fhir-code-systems';
7
8
  export * from './fhir-resource-types';
8
9
  export * from './fhir-versions';
@@ -3,6 +3,7 @@ export * from './communication.js';
3
3
  export * from './cryptography.js';
4
4
  export * from './device.js';
5
5
  export * from './did-services.js';
6
+ export * from './eu-countries.js';
6
7
  export * from './fhir-code-systems.js';
7
8
  export * from './fhir-resource-types.js';
8
9
  export * from './fhir-versions.js';
@@ -1,5 +1,6 @@
1
1
  import { ParameterData } from "../models/params";
2
2
  export declare enum ClaimsServiceSchemaorg {
3
+ areaServed = "org.schema.Service.areaServed",
3
4
  category = "org.schema.Service.category",
4
5
  identifier = "org.schema.Service.identifier",
5
6
  serviceType = "org.schema.Service.serviceType",
@@ -2,6 +2,7 @@
2
2
  // File: src/models/schemaorg.ts
3
3
  export var ClaimsServiceSchemaorg;
4
4
  (function (ClaimsServiceSchemaorg) {
5
+ ClaimsServiceSchemaorg["areaServed"] = "org.schema.Service.areaServed";
5
6
  ClaimsServiceSchemaorg["category"] = "org.schema.Service.category";
6
7
  ClaimsServiceSchemaorg["identifier"] = "org.schema.Service.identifier";
7
8
  ClaimsServiceSchemaorg["serviceType"] = "org.schema.Service.serviceType";
@@ -75,3 +75,8 @@ export declare function getServiceCapabilityFamily(value: string | undefined): s
75
75
  * family.
76
76
  */
77
77
  export declare function hasServiceCapabilityFamily(value: unknown, family: ServiceCapabilityFamilyValue | string): boolean;
78
+ /**
79
+ * Returns whether a capability token denotes a discoverable provider/service
80
+ * role rather than a reader-only role.
81
+ */
82
+ export declare function isProviderServiceCapability(value: string | undefined | null): boolean;
@@ -94,3 +94,12 @@ export function hasServiceCapabilityFamily(value, family) {
94
94
  return false;
95
95
  return parseServiceCapabilityTokens(value).some((item) => getServiceCapabilityFamily(item) === normalizedFamily);
96
96
  }
97
+ /**
98
+ * Returns whether a capability token denotes a discoverable provider/service
99
+ * role rather than a reader-only role.
100
+ */
101
+ export function isProviderServiceCapability(value) {
102
+ const normalized = String(value || '').trim().toLowerCase();
103
+ return normalized === ServiceCapabilityToken.IndexProvider
104
+ || normalized === ServiceCapabilityToken.DigitalTwinProvider;
105
+ }
@@ -0,0 +1,88 @@
1
+ import type { HostingOperatorDiscoveryCatalog, PublishedProviderCatalogRecord } from '../models/dataspace-discovery';
2
+ export type ExampleDataspaceCredentialSubjectInput = Readonly<{
3
+ did?: string;
4
+ serviceTypes?: readonly string[];
5
+ categories?: readonly string[];
6
+ areaServed?: readonly string[];
7
+ addressCountry?: string;
8
+ }>;
9
+ /**
10
+ * Builds a synthetic hosting-operator semantic `credentialSubject`.
11
+ *
12
+ * This example is parameterized on purpose: public docs/tests must not hardcode
13
+ * business identities when demonstrating dataspace discovery semantics.
14
+ *
15
+ * @param input Optional overrides for the synthetic subject.
16
+ * @returns Schema.org-shaped semantic subject with service metadata.
17
+ */
18
+ export declare function buildExampleHostingOperatorCredentialSubject(input?: ExampleDataspaceCredentialSubjectInput): {
19
+ id: string;
20
+ serviceType: string;
21
+ category: string;
22
+ areaServed: {
23
+ '@type': string;
24
+ name: string;
25
+ }[];
26
+ address: {
27
+ addressCountry: string;
28
+ };
29
+ };
30
+ /**
31
+ * Builds a synthetic tenant-service semantic `credentialSubject`.
32
+ *
33
+ * @param input Optional overrides for the synthetic tenant subject.
34
+ * @returns Schema.org-shaped semantic subject with public service metadata.
35
+ */
36
+ export declare function buildExampleTenantServiceCredentialSubject(input?: ExampleDataspaceCredentialSubjectInput): {
37
+ id: string;
38
+ serviceType: string;
39
+ category: string;
40
+ areaServed: {
41
+ '@type': string;
42
+ name: string;
43
+ }[];
44
+ address: {
45
+ addressCountry: string;
46
+ };
47
+ };
48
+ /**
49
+ * Builds the flattened `meta.claims` projection for a hosting-operator semantic
50
+ * subject.
51
+ *
52
+ * @param input Optional overrides for the synthetic projection.
53
+ * @returns Flat operational claims derived from the semantic subject.
54
+ */
55
+ export declare function buildExampleHostingOperatorMetaClaims(input?: ExampleDataspaceCredentialSubjectInput): {
56
+ "org.schema.Service.serviceType": string | undefined;
57
+ "org.schema.Service.category": string;
58
+ "org.schema.Service.areaServed": string;
59
+ "org.schema.Organization.address.addressCountry": string;
60
+ };
61
+ /**
62
+ * Builds the flattened `meta.claims` projection for a tenant-service semantic
63
+ * subject.
64
+ *
65
+ * @param input Optional overrides for the synthetic projection.
66
+ * @returns Flat operational claims derived from the semantic subject.
67
+ */
68
+ export declare function buildExampleTenantServiceMetaClaims(input?: ExampleDataspaceCredentialSubjectInput): {
69
+ "org.schema.Service.serviceType": string | undefined;
70
+ "org.schema.Service.category": string;
71
+ "org.schema.Service.areaServed": string;
72
+ "org.schema.Organization.address.addressCountry": string;
73
+ };
74
+ /**
75
+ * Builds a synthetic published-provider record as it would appear in a host
76
+ * service-autodiscovery catalog.
77
+ *
78
+ * @param input Optional overrides for the synthetic provider publication.
79
+ * @returns Shared host-catalog provider entry.
80
+ */
81
+ export declare function buildExamplePublishedProviderCatalogRecord(input?: ExampleDataspaceCredentialSubjectInput): PublishedProviderCatalogRecord;
82
+ /**
83
+ * Builds a synthetic host/operator service-autodiscovery catalog.
84
+ *
85
+ * @param providers Optional published providers to include.
86
+ * @returns Shared catalog DTO for host-side public service autodiscovery.
87
+ */
88
+ export declare function buildExampleHostingOperatorDiscoveryCatalog(providers?: ReadonlyArray<PublishedProviderCatalogRecord>): HostingOperatorDiscoveryCatalog;
@@ -0,0 +1,129 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ import { ClaimsOrganizationSchemaorg, ClaimsServiceSchemaorg } from '../constants/schemaorg.js';
3
+ import { serializeServiceCapabilityTokens, ServiceCapabilityToken } from '../constants/service-capabilities.js';
4
+ import { EXAMPLE_HOSTING_OPERATOR_CATALOG_URL, EXAMPLE_HOSTING_OPERATOR_DID, EXAMPLE_PROVIDER_PUBLISHED_ENDPOINT_URL, EXAMPLE_JURISDICTION, EXAMPLE_SECTOR, EXAMPLE_TENANT_SERVICE_DID, } from './shared.js';
5
+ function firstOrCsv(values) {
6
+ return values.length <= 1 ? (values[0] || '') : values.join(',');
7
+ }
8
+ /**
9
+ * Builds a synthetic hosting-operator semantic `credentialSubject`.
10
+ *
11
+ * This example is parameterized on purpose: public docs/tests must not hardcode
12
+ * business identities when demonstrating dataspace discovery semantics.
13
+ *
14
+ * @param input Optional overrides for the synthetic subject.
15
+ * @returns Schema.org-shaped semantic subject with service metadata.
16
+ */
17
+ export function buildExampleHostingOperatorCredentialSubject(input = {}) {
18
+ const serviceTypes = input.serviceTypes || [
19
+ ServiceCapabilityToken.IndexProvider,
20
+ ServiceCapabilityToken.DigitalTwinProvider,
21
+ ];
22
+ const categories = input.categories || [EXAMPLE_SECTOR];
23
+ const areaServed = input.areaServed || ['EU', EXAMPLE_JURISDICTION];
24
+ const addressCountry = input.addressCountry || EXAMPLE_JURISDICTION;
25
+ return {
26
+ id: input.did || 'did:web:host.example.org',
27
+ serviceType: firstOrCsv(serviceTypes),
28
+ category: firstOrCsv(categories),
29
+ areaServed: areaServed.map((name) => ({ '@type': 'AdministrativeArea', name })),
30
+ address: {
31
+ addressCountry,
32
+ },
33
+ };
34
+ }
35
+ /**
36
+ * Builds a synthetic tenant-service semantic `credentialSubject`.
37
+ *
38
+ * @param input Optional overrides for the synthetic tenant subject.
39
+ * @returns Schema.org-shaped semantic subject with public service metadata.
40
+ */
41
+ export function buildExampleTenantServiceCredentialSubject(input = {}) {
42
+ const serviceTypes = input.serviceTypes || [ServiceCapabilityToken.IndexProvider];
43
+ const categories = input.categories || [EXAMPLE_SECTOR];
44
+ const areaServed = input.areaServed || ['EU'];
45
+ const addressCountry = input.addressCountry || EXAMPLE_JURISDICTION;
46
+ return {
47
+ id: input.did || 'did:web:provider.example.org',
48
+ serviceType: firstOrCsv(serviceTypes),
49
+ category: firstOrCsv(categories),
50
+ areaServed: areaServed.map((name) => ({ '@type': 'AdministrativeArea', name })),
51
+ address: {
52
+ addressCountry,
53
+ },
54
+ };
55
+ }
56
+ /**
57
+ * Builds the flattened `meta.claims` projection for a hosting-operator semantic
58
+ * subject.
59
+ *
60
+ * @param input Optional overrides for the synthetic projection.
61
+ * @returns Flat operational claims derived from the semantic subject.
62
+ */
63
+ export function buildExampleHostingOperatorMetaClaims(input = {}) {
64
+ const serviceTypes = input.serviceTypes || [
65
+ ServiceCapabilityToken.IndexProvider,
66
+ ServiceCapabilityToken.DigitalTwinProvider,
67
+ ];
68
+ const categories = input.categories || [EXAMPLE_SECTOR];
69
+ const areaServed = input.areaServed || ['EU', EXAMPLE_JURISDICTION];
70
+ const addressCountry = input.addressCountry || EXAMPLE_JURISDICTION;
71
+ return {
72
+ [ClaimsServiceSchemaorg.serviceType]: serializeServiceCapabilityTokens(serviceTypes),
73
+ [ClaimsServiceSchemaorg.category]: firstOrCsv(categories),
74
+ [ClaimsServiceSchemaorg.areaServed]: firstOrCsv(areaServed),
75
+ [ClaimsOrganizationSchemaorg.addressCountry]: addressCountry,
76
+ };
77
+ }
78
+ /**
79
+ * Builds the flattened `meta.claims` projection for a tenant-service semantic
80
+ * subject.
81
+ *
82
+ * @param input Optional overrides for the synthetic projection.
83
+ * @returns Flat operational claims derived from the semantic subject.
84
+ */
85
+ export function buildExampleTenantServiceMetaClaims(input = {}) {
86
+ const serviceTypes = input.serviceTypes || [ServiceCapabilityToken.IndexProvider];
87
+ const categories = input.categories || [EXAMPLE_SECTOR];
88
+ const areaServed = input.areaServed || ['EU'];
89
+ const addressCountry = input.addressCountry || EXAMPLE_JURISDICTION;
90
+ return {
91
+ [ClaimsServiceSchemaorg.serviceType]: serializeServiceCapabilityTokens(serviceTypes),
92
+ [ClaimsServiceSchemaorg.category]: firstOrCsv(categories),
93
+ [ClaimsServiceSchemaorg.areaServed]: firstOrCsv(areaServed),
94
+ [ClaimsOrganizationSchemaorg.addressCountry]: addressCountry,
95
+ };
96
+ }
97
+ /**
98
+ * Builds a synthetic published-provider record as it would appear in a host
99
+ * service-autodiscovery catalog.
100
+ *
101
+ * @param input Optional overrides for the synthetic provider publication.
102
+ * @returns Shared host-catalog provider entry.
103
+ */
104
+ export function buildExamplePublishedProviderCatalogRecord(input = {}) {
105
+ const serviceTypes = input.serviceTypes || [ServiceCapabilityToken.IndexProvider];
106
+ const categories = input.categories || [EXAMPLE_SECTOR];
107
+ const areaServed = input.areaServed || ['EU'];
108
+ return {
109
+ providerDid: input.did || EXAMPLE_TENANT_SERVICE_DID,
110
+ serviceType: serviceTypes[0] || ServiceCapabilityToken.IndexProvider,
111
+ category: categories[0] || EXAMPLE_SECTOR,
112
+ areaServed: areaServed[0] || 'EU',
113
+ endpointUrl: EXAMPLE_PROVIDER_PUBLISHED_ENDPOINT_URL,
114
+ catalogUrl: EXAMPLE_HOSTING_OPERATOR_CATALOG_URL,
115
+ };
116
+ }
117
+ /**
118
+ * Builds a synthetic host/operator service-autodiscovery catalog.
119
+ *
120
+ * @param providers Optional published providers to include.
121
+ * @returns Shared catalog DTO for host-side public service autodiscovery.
122
+ */
123
+ export function buildExampleHostingOperatorDiscoveryCatalog(providers = [buildExamplePublishedProviderCatalogRecord()]) {
124
+ return {
125
+ hostingOperatorDid: EXAMPLE_HOSTING_OPERATOR_DID,
126
+ catalogUrl: EXAMPLE_HOSTING_OPERATOR_CATALOG_URL,
127
+ providers: [...providers],
128
+ };
129
+ }
@@ -1,5 +1,6 @@
1
1
  export * from './shared';
2
2
  export * from './ica-activation-proof';
3
+ export * from './dataspace-discovery';
3
4
  export * from './organization-controller';
4
5
  export * from './individual-controller';
5
6
  export * from './professional';
@@ -1,5 +1,6 @@
1
1
  export * from './shared.js';
2
2
  export * from './ica-activation-proof.js';
3
+ export * from './dataspace-discovery.js';
3
4
  export * from './organization-controller.js';
4
5
  export * from './individual-controller.js';
5
6
  export * from './professional.js';
@@ -31,6 +31,19 @@ export declare const EXAMPLE_SUBJECT_DID: "did:web:api.acme.org:individual:123";
31
31
  export declare const EXAMPLE_PROFESSIONAL_DID: "did:web:api.acme.org:professional:1";
32
32
  export declare const EXAMPLE_PROVIDER_ORGANIZATION_DID: "did:web:hospital.acme.org";
33
33
  export declare const EXAMPLE_PROVIDER_ORGANIZATION_URL: "https://hospital.acme.org";
34
+ export declare const EXAMPLE_GATEWAY_PUBLIC_ORIGIN: "https://gateway.example.com";
35
+ export declare const EXAMPLE_HOST_PUBLIC_HOSTNAME: "host.example.com";
36
+ export declare const EXAMPLE_HOSTING_OPERATOR_DID: "did:web:host.example.org";
37
+ export declare const EXAMPLE_TENANT_SERVICE_DID: "did:web:provider.example.org";
38
+ export declare const EXAMPLE_SECONDARY_TENANT_SERVICE_DID: "did:web:provider-b.example.org";
39
+ export declare const EXAMPLE_HOSTING_OPERATOR_CATALOG_URL: "https://host.example.org/.well-known/dcat3/catalog";
40
+ export declare const EXAMPLE_PROVIDER_PUBLISHED_ENDPOINT_URL: "https://host.example.org/catalog/provider-a";
41
+ export declare const EXAMPLE_PROVIDER_LEGAL_NAME: "ACME Health Provider";
42
+ export declare const EXAMPLE_SECONDARY_PROVIDER_LEGAL_NAME: "Reader Only Provider";
43
+ export declare const EXAMPLE_SECONDARY_PROVIDER_ALTERNATE_NAME: "reader-only";
44
+ export declare const EXAMPLE_COVERAGE_SCOPE_EU: "EU";
45
+ export declare const EXAMPLE_NON_EU_COUNTRY: "US";
46
+ export declare const EXAMPLE_SECONDARY_EU_COUNTRY: "PT";
34
47
  export declare const EXAMPLE_PATIENT_DID: "did:web:patient.example";
35
48
  export declare const EXAMPLE_PROFILE_PROVIDER_DID: "did:web:provider.example.org";
36
49
  export declare const EXAMPLE_PROFILE_ORGANIZATION_DID: "did:web:org.example";
@@ -183,3 +196,10 @@ export declare function buildExampleDocumentReferenceSearchPayload(subjectDid?:
183
196
  };
184
197
  };
185
198
  export declare function cloneExample<T>(value: T): T;
199
+ export type ExampleHostedTenantRouteContext = Readonly<{
200
+ alternateName: string;
201
+ jurisdiction: string;
202
+ version: string;
203
+ sector: string;
204
+ }>;
205
+ export declare function buildExampleHostedTenantBaseUrl(input: ExampleHostedTenantRouteContext): string;
@@ -36,6 +36,19 @@ export const EXAMPLE_SUBJECT_DID = 'did:web:api.acme.org:individual:123';
36
36
  export const EXAMPLE_PROFESSIONAL_DID = 'did:web:api.acme.org:professional:1';
37
37
  export const EXAMPLE_PROVIDER_ORGANIZATION_DID = 'did:web:hospital.acme.org';
38
38
  export const EXAMPLE_PROVIDER_ORGANIZATION_URL = 'https://hospital.acme.org';
39
+ export const EXAMPLE_GATEWAY_PUBLIC_ORIGIN = 'https://gateway.example.com';
40
+ export const EXAMPLE_HOST_PUBLIC_HOSTNAME = 'host.example.com';
41
+ export const EXAMPLE_HOSTING_OPERATOR_DID = 'did:web:host.example.org';
42
+ export const EXAMPLE_TENANT_SERVICE_DID = 'did:web:provider.example.org';
43
+ export const EXAMPLE_SECONDARY_TENANT_SERVICE_DID = 'did:web:provider-b.example.org';
44
+ export const EXAMPLE_HOSTING_OPERATOR_CATALOG_URL = 'https://host.example.org/.well-known/dcat3/catalog';
45
+ export const EXAMPLE_PROVIDER_PUBLISHED_ENDPOINT_URL = 'https://host.example.org/catalog/provider-a';
46
+ export const EXAMPLE_PROVIDER_LEGAL_NAME = 'ACME Health Provider';
47
+ export const EXAMPLE_SECONDARY_PROVIDER_LEGAL_NAME = 'Reader Only Provider';
48
+ export const EXAMPLE_SECONDARY_PROVIDER_ALTERNATE_NAME = 'reader-only';
49
+ export const EXAMPLE_COVERAGE_SCOPE_EU = 'EU';
50
+ export const EXAMPLE_NON_EU_COUNTRY = 'US';
51
+ export const EXAMPLE_SECONDARY_EU_COUNTRY = 'PT';
39
52
  export const EXAMPLE_PATIENT_DID = 'did:web:patient.example';
40
53
  export const EXAMPLE_PROFILE_PROVIDER_DID = 'did:web:provider.example.org';
41
54
  export const EXAMPLE_PROFILE_ORGANIZATION_DID = 'did:web:org.example';
@@ -157,3 +170,6 @@ export function buildExampleDocumentReferenceSearchPayload(subjectDid = EXAMPLE_
157
170
  export function cloneExample(value) {
158
171
  return JSON.parse(JSON.stringify(value));
159
172
  }
173
+ export function buildExampleHostedTenantBaseUrl(input) {
174
+ return `${EXAMPLE_GATEWAY_PUBLIC_ORIGIN}/${input.alternateName}/cds-${input.jurisdiction.toLowerCase()}/${input.version}/${input.sector}`;
175
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Canonical shared coverage scopes derived from semantic service metadata.
3
+ *
4
+ * `EU` is a coverage scope, not a sector.
5
+ */
6
+ export declare const DataspaceCoverageScope: {
7
+ readonly EuropeanUnion: "EU";
8
+ };
9
+ export type DataspaceCoverageScopeValue = typeof DataspaceCoverageScope[keyof typeof DataspaceCoverageScope];
10
+ /**
11
+ * Runtime-neutral service-discovery record normalized from a semantic
12
+ * `credentialSubject` and optionally its flattened `meta.claims` projection.
13
+ */
14
+ export type DataspaceServiceSemanticRecord = Readonly<{
15
+ subjectId?: string;
16
+ serviceTypes: string[];
17
+ categories: string[];
18
+ areaServed: string[];
19
+ addressCountry?: string;
20
+ coverageScope?: string;
21
+ }>;
22
+ /**
23
+ * Semantic hosting-operator record extracted from an ICA-issued VC or
24
+ * equivalent semantic payload.
25
+ */
26
+ export type HostingOperatorSemanticRecord = DataspaceServiceSemanticRecord;
27
+ /**
28
+ * Semantic tenant-service record extracted from an ICA-issued VC or equivalent
29
+ * semantic payload.
30
+ */
31
+ export type TenantServiceSemanticRecord = DataspaceServiceSemanticRecord;
32
+ /**
33
+ * Public provider entry expected from a host discovery catalog.
34
+ */
35
+ export type PublishedProviderCatalogRecord = Readonly<{
36
+ providerDid: string;
37
+ serviceType: string;
38
+ category: string;
39
+ areaServed?: string;
40
+ endpointUrl?: string;
41
+ catalogUrl?: string;
42
+ }>;
43
+ /**
44
+ * Shared host-catalog filter for service autodiscovery.
45
+ *
46
+ * This shape is runtime-neutral and can be reused by GW, ICA, backend SDKs,
47
+ * and portal/native-app backends.
48
+ */
49
+ export type DataspaceDiscoveryFilter = Readonly<{
50
+ sector: string;
51
+ capability?: string;
52
+ requiredCapabilities?: readonly string[];
53
+ jurisdiction?: string;
54
+ coverageScope?: string;
55
+ }>;
56
+ /**
57
+ * Public host/operator service-autodiscovery catalog.
58
+ *
59
+ * This is distinct from any dataset-specific catalog exposed by an individual
60
+ * `DigitalTwinProvider`.
61
+ */
62
+ export type HostingOperatorDiscoveryCatalog = Readonly<{
63
+ hostingOperatorDid?: string;
64
+ catalogUrl?: string;
65
+ providers: PublishedProviderCatalogRecord[];
66
+ }>;
@@ -0,0 +1,9 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ /**
3
+ * Canonical shared coverage scopes derived from semantic service metadata.
4
+ *
5
+ * `EU` is a coverage scope, not a sector.
6
+ */
7
+ export const DataspaceCoverageScope = {
8
+ EuropeanUnion: 'EU',
9
+ };
@@ -12,6 +12,7 @@ export * from './confidential-storage';
12
12
  export * from './consent-rule';
13
13
  export * from './consent-access';
14
14
  export * from './crypto';
15
+ export * from './dataspace-discovery';
15
16
  export * from './device-license';
16
17
  export * from './did';
17
18
  export * from './fhir-documents';
@@ -12,6 +12,7 @@ export * from './confidential-storage.js';
12
12
  export * from './consent-rule.js';
13
13
  export * from './consent-access.js';
14
14
  export * from './crypto.js';
15
+ export * from './dataspace-discovery.js';
15
16
  export * from './device-license.js';
16
17
  export * from './did.js';
17
18
  export * from './fhir-documents.js';
@@ -0,0 +1,146 @@
1
+ import { type DataspaceDiscoveryFilter, type DataspaceServiceSemanticRecord, type HostingOperatorSemanticRecord, type HostingOperatorDiscoveryCatalog, type PublishedProviderCatalogRecord, type TenantServiceSemanticRecord } from '../models/dataspace-discovery';
2
+ /**
3
+ * Parses the CSV or array representation of `serviceType`.
4
+ *
5
+ * @param value Semantic `credentialSubject.serviceType` or flattened
6
+ * `meta.claims['org.schema.Service.serviceType']`.
7
+ * @returns Normalized unique service capability tokens.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * parseServiceTypeCsv('indexing.cruds,digitaltwin.rs');
12
+ * // ['indexing.cruds', 'digitaltwin.rs']
13
+ * ```
14
+ */
15
+ export declare function parseServiceTypeCsv(value: unknown): string[];
16
+ /**
17
+ * Parses the Schema.org `category` service dimension used as the dataspace
18
+ * sector vocabulary in the current profile.
19
+ *
20
+ * @param value Semantic `credentialSubject.category` or flattened
21
+ * `meta.claims['org.schema.Service.category']`.
22
+ * @returns Normalized unique sector/category values.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * parseServiceCategories('animal-care,health-care');
27
+ * // ['animal-care', 'health-care']
28
+ * ```
29
+ */
30
+ export declare function parseServiceCategories(value: unknown): string[];
31
+ /**
32
+ * Parses Schema.org `areaServed` values.
33
+ *
34
+ * Accepts scalar strings, CSV strings, arrays, and simple
35
+ * `AdministrativeArea`-like objects with `name` or `@id`.
36
+ *
37
+ * @param value Semantic `credentialSubject.areaServed` or flattened
38
+ * `meta.claims['org.schema.Service.areaServed']`.
39
+ * @returns Normalized unique coverage values.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * parseAreaServed([{ '@type': 'AdministrativeArea', name: 'EU' }, 'ES']);
44
+ * // ['EU', 'ES']
45
+ * ```
46
+ */
47
+ export declare function parseAreaServed(value: unknown): string[];
48
+ /**
49
+ * Infers a broader coverage scope from an ISO-2 country code.
50
+ *
51
+ * `EU` is returned only as a coverage scope. It must not be treated as a
52
+ * sector.
53
+ *
54
+ * @param countryCode ISO-2 country code, typically from
55
+ * `credentialSubject.address.addressCountry`.
56
+ * @returns `EU` for EU member countries, otherwise the normalized country code.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * inferCoverageScopeFromCountryCode('ES');
61
+ * // 'EU'
62
+ * ```
63
+ */
64
+ export declare function inferCoverageScopeFromCountryCode(countryCode: string | undefined | null): string | undefined;
65
+ /**
66
+ * Infers a coverage scope from the semantic `credentialSubject`.
67
+ *
68
+ * @param subject Semantic service object containing `address.addressCountry`.
69
+ * @returns Broader coverage scope such as `EU`, or the normalized country code
70
+ * when the country is outside the EU set.
71
+ */
72
+ export declare function inferCoverageScopeFromCredentialSubject(subject: unknown): string | undefined;
73
+ /**
74
+ * Extracts the shared dataspace service semantics from a VC-like payload.
75
+ *
76
+ * Source-of-truth rule:
77
+ * - semantic values come from `credentialSubject` first
78
+ * - flattened `meta.claims` is accepted as a compatibility projection/fallback
79
+ * - when both exist they must agree
80
+ *
81
+ * @param input VC-like payload, direct semantic object, or equivalent DTO.
82
+ * @returns Runtime-neutral normalized dataspace discovery semantics.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * extractDataspaceServiceSemanticRecord({
87
+ * credentialSubject: {
88
+ * id: 'did:web:provider.example.org',
89
+ * serviceType: 'indexing.cruds',
90
+ * category: 'animal-care',
91
+ * areaServed: { '@type': 'AdministrativeArea', name: 'EU' },
92
+ * address: { addressCountry: 'ES' },
93
+ * },
94
+ * meta: {
95
+ * claims: {
96
+ * 'org.schema.Service.serviceType': 'indexing.cruds',
97
+ * 'org.schema.Service.category': 'animal-care',
98
+ * 'org.schema.Service.areaServed': 'EU',
99
+ * 'org.schema.Organization.address.addressCountry': 'ES',
100
+ * },
101
+ * },
102
+ * });
103
+ * ```
104
+ */
105
+ export declare function extractDataspaceServiceSemanticRecord(input: unknown): DataspaceServiceSemanticRecord;
106
+ /**
107
+ * Extracts a hosting-operator semantic record from a VC-like payload.
108
+ *
109
+ * @param input VC-like payload or direct semantic object.
110
+ * @returns Hosting-operator semantic record normalized with the common
111
+ * dataspace extraction rules.
112
+ */
113
+ export declare function extractHostingOperatorSemanticRecord(input: unknown): HostingOperatorSemanticRecord;
114
+ /**
115
+ * Extracts a tenant-service semantic record from a VC-like payload.
116
+ *
117
+ * @param input VC-like payload or direct semantic object.
118
+ * @returns Tenant-service semantic record normalized with the common dataspace
119
+ * extraction rules.
120
+ */
121
+ export declare function extractTenantServiceSemanticRecord(input: unknown): TenantServiceSemanticRecord;
122
+ /**
123
+ * Returns whether a normalized hosting-operator record satisfies a service
124
+ * autodiscovery filter.
125
+ */
126
+ export declare function matchesHostingOperatorDiscoveryFilter(record: HostingOperatorSemanticRecord, filter: DataspaceDiscoveryFilter): boolean;
127
+ /**
128
+ * Returns whether a published provider catalog entry satisfies a service
129
+ * autodiscovery filter.
130
+ */
131
+ export declare function matchesPublishedProviderDiscoveryFilter(record: PublishedProviderCatalogRecord, filter: DataspaceDiscoveryFilter): boolean;
132
+ /**
133
+ * Filters hosting-operator records using the shared service-autodiscovery
134
+ * semantics.
135
+ */
136
+ export declare function filterHostingOperatorsByDiscoveryFilter(records: ReadonlyArray<HostingOperatorSemanticRecord>, filter: DataspaceDiscoveryFilter): HostingOperatorSemanticRecord[];
137
+ /**
138
+ * Filters published provider entries using the shared service-autodiscovery
139
+ * semantics.
140
+ */
141
+ export declare function filterPublishedProvidersByDiscoveryFilter(records: ReadonlyArray<PublishedProviderCatalogRecord>, filter: DataspaceDiscoveryFilter): PublishedProviderCatalogRecord[];
142
+ /**
143
+ * Filters a host/operator discovery catalog down to the providers that satisfy
144
+ * the requested service-autodiscovery filter.
145
+ */
146
+ export declare function filterHostingOperatorDiscoveryCatalog(catalog: HostingOperatorDiscoveryCatalog, filter: DataspaceDiscoveryFilter): HostingOperatorDiscoveryCatalog;
@@ -0,0 +1,341 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ import { ClaimsOrganizationSchemaorg, ClaimsServiceSchemaorg } from '../constants/schemaorg.js';
3
+ import { isEuCountryCode, normalizeCountryCode } from '../constants/eu-countries.js';
4
+ import { isProviderServiceCapability } from '../constants/service-capabilities.js';
5
+ import { DataspaceCoverageScope, } from '../models/dataspace-discovery.js';
6
+ function asObject(value) {
7
+ return value && typeof value === 'object' && !Array.isArray(value)
8
+ ? value
9
+ : undefined;
10
+ }
11
+ function asNonEmptyString(value) {
12
+ return typeof value === 'string' ? value.trim() : '';
13
+ }
14
+ function toStringList(value) {
15
+ if (Array.isArray(value)) {
16
+ return Array.from(new Set(value
17
+ .flatMap((entry) => toStringList(entry))
18
+ .map((entry) => entry.trim())
19
+ .filter(Boolean)));
20
+ }
21
+ const raw = asNonEmptyString(value);
22
+ if (!raw)
23
+ return [];
24
+ return Array.from(new Set(raw
25
+ .split(',')
26
+ .map((entry) => entry.trim())
27
+ .filter(Boolean)));
28
+ }
29
+ function normalizeList(values) {
30
+ return Array.from(new Set(values
31
+ .map((value) => value.trim())
32
+ .filter(Boolean)));
33
+ }
34
+ function sameNormalizedList(left, right) {
35
+ if (left.length !== right.length)
36
+ return false;
37
+ const normalizedLeft = [...normalizeList(left)].sort();
38
+ const normalizedRight = [...normalizeList(right)].sort();
39
+ return normalizedLeft.every((value, index) => value === normalizedRight[index]);
40
+ }
41
+ function parseAreaServedValue(value) {
42
+ if (Array.isArray(value)) {
43
+ return normalizeList(value.flatMap((entry) => parseAreaServedValue(entry)));
44
+ }
45
+ if (typeof value === 'string') {
46
+ return toStringList(value);
47
+ }
48
+ const objectValue = asObject(value);
49
+ if (!objectValue)
50
+ return [];
51
+ return normalizeList([
52
+ asNonEmptyString(objectValue.name),
53
+ asNonEmptyString(objectValue['@id']),
54
+ asNonEmptyString(objectValue.id),
55
+ ].filter(Boolean));
56
+ }
57
+ function getSemanticCredentialSubject(input) {
58
+ const objectInput = asObject(input);
59
+ if (!objectInput)
60
+ return undefined;
61
+ const credentialSubject = asObject(objectInput.credentialSubject);
62
+ if (credentialSubject)
63
+ return credentialSubject;
64
+ return objectInput;
65
+ }
66
+ function getFlattenedClaims(input) {
67
+ const objectInput = asObject(input);
68
+ if (!objectInput)
69
+ return undefined;
70
+ const meta = asObject(objectInput.meta);
71
+ const claims = asObject(meta?.claims);
72
+ return claims;
73
+ }
74
+ function getSemanticServiceTypes(subject) {
75
+ return toStringList(subject?.serviceType);
76
+ }
77
+ function getSemanticCategories(subject) {
78
+ return toStringList(subject?.category);
79
+ }
80
+ function getSemanticAreaServed(subject) {
81
+ return parseAreaServedValue(subject?.areaServed);
82
+ }
83
+ function getSemanticAddressCountry(subject) {
84
+ const address = asObject(subject?.address);
85
+ return normalizeCountryCode(asNonEmptyString(address?.addressCountry));
86
+ }
87
+ function getFlattenedServiceTypes(claims) {
88
+ return toStringList(claims?.[ClaimsServiceSchemaorg.serviceType]);
89
+ }
90
+ function getFlattenedCategories(claims) {
91
+ return toStringList(claims?.[ClaimsServiceSchemaorg.category]);
92
+ }
93
+ function getFlattenedAreaServed(claims) {
94
+ return parseAreaServedValue(claims?.[ClaimsServiceSchemaorg.areaServed]);
95
+ }
96
+ function getFlattenedAddressCountry(claims) {
97
+ return normalizeCountryCode(asNonEmptyString(claims?.[ClaimsOrganizationSchemaorg.addressCountry]));
98
+ }
99
+ function assertNoMismatch(kind, semantic, flattened) {
100
+ if (!semantic.length || !flattened.length)
101
+ return;
102
+ if (!sameNormalizedList(semantic, flattened)) {
103
+ throw new Error(`Dataspace discovery mismatch for ${kind}: credentialSubject and meta.claims disagree.`);
104
+ }
105
+ }
106
+ function assertNoScalarMismatch(kind, semantic, flattened) {
107
+ if (!semantic || !flattened)
108
+ return;
109
+ if (semantic !== flattened) {
110
+ throw new Error(`Dataspace discovery mismatch for ${kind}: credentialSubject and meta.claims disagree.`);
111
+ }
112
+ }
113
+ /**
114
+ * Parses the CSV or array representation of `serviceType`.
115
+ *
116
+ * @param value Semantic `credentialSubject.serviceType` or flattened
117
+ * `meta.claims['org.schema.Service.serviceType']`.
118
+ * @returns Normalized unique service capability tokens.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * parseServiceTypeCsv('indexing.cruds,digitaltwin.rs');
123
+ * // ['indexing.cruds', 'digitaltwin.rs']
124
+ * ```
125
+ */
126
+ export function parseServiceTypeCsv(value) {
127
+ return toStringList(value);
128
+ }
129
+ /**
130
+ * Parses the Schema.org `category` service dimension used as the dataspace
131
+ * sector vocabulary in the current profile.
132
+ *
133
+ * @param value Semantic `credentialSubject.category` or flattened
134
+ * `meta.claims['org.schema.Service.category']`.
135
+ * @returns Normalized unique sector/category values.
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * parseServiceCategories('animal-care,health-care');
140
+ * // ['animal-care', 'health-care']
141
+ * ```
142
+ */
143
+ export function parseServiceCategories(value) {
144
+ return toStringList(value);
145
+ }
146
+ /**
147
+ * Parses Schema.org `areaServed` values.
148
+ *
149
+ * Accepts scalar strings, CSV strings, arrays, and simple
150
+ * `AdministrativeArea`-like objects with `name` or `@id`.
151
+ *
152
+ * @param value Semantic `credentialSubject.areaServed` or flattened
153
+ * `meta.claims['org.schema.Service.areaServed']`.
154
+ * @returns Normalized unique coverage values.
155
+ *
156
+ * @example
157
+ * ```ts
158
+ * parseAreaServed([{ '@type': 'AdministrativeArea', name: 'EU' }, 'ES']);
159
+ * // ['EU', 'ES']
160
+ * ```
161
+ */
162
+ export function parseAreaServed(value) {
163
+ return parseAreaServedValue(value);
164
+ }
165
+ /**
166
+ * Infers a broader coverage scope from an ISO-2 country code.
167
+ *
168
+ * `EU` is returned only as a coverage scope. It must not be treated as a
169
+ * sector.
170
+ *
171
+ * @param countryCode ISO-2 country code, typically from
172
+ * `credentialSubject.address.addressCountry`.
173
+ * @returns `EU` for EU member countries, otherwise the normalized country code.
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * inferCoverageScopeFromCountryCode('ES');
178
+ * // 'EU'
179
+ * ```
180
+ */
181
+ export function inferCoverageScopeFromCountryCode(countryCode) {
182
+ const normalized = normalizeCountryCode(countryCode);
183
+ if (!normalized)
184
+ return undefined;
185
+ return isEuCountryCode(normalized)
186
+ ? DataspaceCoverageScope.EuropeanUnion
187
+ : normalized;
188
+ }
189
+ /**
190
+ * Infers a coverage scope from the semantic `credentialSubject`.
191
+ *
192
+ * @param subject Semantic service object containing `address.addressCountry`.
193
+ * @returns Broader coverage scope such as `EU`, or the normalized country code
194
+ * when the country is outside the EU set.
195
+ */
196
+ export function inferCoverageScopeFromCredentialSubject(subject) {
197
+ const subjectObject = asObject(subject);
198
+ const address = asObject(subjectObject?.address);
199
+ return inferCoverageScopeFromCountryCode(asNonEmptyString(address?.addressCountry));
200
+ }
201
+ /**
202
+ * Extracts the shared dataspace service semantics from a VC-like payload.
203
+ *
204
+ * Source-of-truth rule:
205
+ * - semantic values come from `credentialSubject` first
206
+ * - flattened `meta.claims` is accepted as a compatibility projection/fallback
207
+ * - when both exist they must agree
208
+ *
209
+ * @param input VC-like payload, direct semantic object, or equivalent DTO.
210
+ * @returns Runtime-neutral normalized dataspace discovery semantics.
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * extractDataspaceServiceSemanticRecord({
215
+ * credentialSubject: {
216
+ * id: 'did:web:provider.example.org',
217
+ * serviceType: 'indexing.cruds',
218
+ * category: 'animal-care',
219
+ * areaServed: { '@type': 'AdministrativeArea', name: 'EU' },
220
+ * address: { addressCountry: 'ES' },
221
+ * },
222
+ * meta: {
223
+ * claims: {
224
+ * 'org.schema.Service.serviceType': 'indexing.cruds',
225
+ * 'org.schema.Service.category': 'animal-care',
226
+ * 'org.schema.Service.areaServed': 'EU',
227
+ * 'org.schema.Organization.address.addressCountry': 'ES',
228
+ * },
229
+ * },
230
+ * });
231
+ * ```
232
+ */
233
+ export function extractDataspaceServiceSemanticRecord(input) {
234
+ const subject = getSemanticCredentialSubject(input);
235
+ const claims = getFlattenedClaims(input);
236
+ const semanticServiceTypes = getSemanticServiceTypes(subject);
237
+ const semanticCategories = getSemanticCategories(subject);
238
+ const semanticAreaServed = getSemanticAreaServed(subject);
239
+ const semanticAddressCountry = getSemanticAddressCountry(subject);
240
+ const flattenedServiceTypes = getFlattenedServiceTypes(claims);
241
+ const flattenedCategories = getFlattenedCategories(claims);
242
+ const flattenedAreaServed = getFlattenedAreaServed(claims);
243
+ const flattenedAddressCountry = getFlattenedAddressCountry(claims);
244
+ assertNoMismatch('serviceType', semanticServiceTypes, flattenedServiceTypes);
245
+ assertNoMismatch('category', semanticCategories, flattenedCategories);
246
+ assertNoMismatch('areaServed', semanticAreaServed, flattenedAreaServed);
247
+ assertNoScalarMismatch('address.addressCountry', semanticAddressCountry, flattenedAddressCountry);
248
+ const serviceTypes = semanticServiceTypes.length ? semanticServiceTypes : flattenedServiceTypes;
249
+ const categories = semanticCategories.length ? semanticCategories : flattenedCategories;
250
+ const areaServed = semanticAreaServed.length ? semanticAreaServed : flattenedAreaServed;
251
+ const addressCountry = semanticAddressCountry || flattenedAddressCountry || undefined;
252
+ return {
253
+ subjectId: asNonEmptyString(subject?.id) || undefined,
254
+ serviceTypes,
255
+ categories,
256
+ areaServed,
257
+ addressCountry,
258
+ coverageScope: inferCoverageScopeFromCountryCode(addressCountry),
259
+ };
260
+ }
261
+ /**
262
+ * Extracts a hosting-operator semantic record from a VC-like payload.
263
+ *
264
+ * @param input VC-like payload or direct semantic object.
265
+ * @returns Hosting-operator semantic record normalized with the common
266
+ * dataspace extraction rules.
267
+ */
268
+ export function extractHostingOperatorSemanticRecord(input) {
269
+ return extractDataspaceServiceSemanticRecord(input);
270
+ }
271
+ /**
272
+ * Extracts a tenant-service semantic record from a VC-like payload.
273
+ *
274
+ * @param input VC-like payload or direct semantic object.
275
+ * @returns Tenant-service semantic record normalized with the common dataspace
276
+ * extraction rules.
277
+ */
278
+ export function extractTenantServiceSemanticRecord(input) {
279
+ return extractDataspaceServiceSemanticRecord(input);
280
+ }
281
+ function matchesCoverageFilter(areaServed, jurisdiction, coverageScope) {
282
+ const normalizedAreaServed = normalizeList([...(areaServed || [])]);
283
+ if (jurisdiction && !normalizedAreaServed.includes(jurisdiction))
284
+ return false;
285
+ if (coverageScope && !normalizedAreaServed.includes(coverageScope))
286
+ return false;
287
+ return true;
288
+ }
289
+ /**
290
+ * Returns whether a normalized hosting-operator record satisfies a service
291
+ * autodiscovery filter.
292
+ */
293
+ export function matchesHostingOperatorDiscoveryFilter(record, filter) {
294
+ if (!record.categories.includes(filter.sector))
295
+ return false;
296
+ if (!matchesCoverageFilter(record.areaServed, filter.jurisdiction, filter.coverageScope))
297
+ return false;
298
+ if (filter.capability && !record.serviceTypes.includes(filter.capability))
299
+ return false;
300
+ if (filter.requiredCapabilities?.length) {
301
+ return filter.requiredCapabilities.every((capability) => record.serviceTypes.includes(capability));
302
+ }
303
+ return true;
304
+ }
305
+ /**
306
+ * Returns whether a published provider catalog entry satisfies a service
307
+ * autodiscovery filter.
308
+ */
309
+ export function matchesPublishedProviderDiscoveryFilter(record, filter) {
310
+ if (!isProviderServiceCapability(record.serviceType))
311
+ return false;
312
+ if (record.category !== filter.sector)
313
+ return false;
314
+ if (filter.capability && record.serviceType !== filter.capability)
315
+ return false;
316
+ return matchesCoverageFilter(record.areaServed ? [record.areaServed] : [], filter.jurisdiction, filter.coverageScope);
317
+ }
318
+ /**
319
+ * Filters hosting-operator records using the shared service-autodiscovery
320
+ * semantics.
321
+ */
322
+ export function filterHostingOperatorsByDiscoveryFilter(records, filter) {
323
+ return records.filter((record) => matchesHostingOperatorDiscoveryFilter(record, filter));
324
+ }
325
+ /**
326
+ * Filters published provider entries using the shared service-autodiscovery
327
+ * semantics.
328
+ */
329
+ export function filterPublishedProvidersByDiscoveryFilter(records, filter) {
330
+ return records.filter((record) => matchesPublishedProviderDiscoveryFilter(record, filter));
331
+ }
332
+ /**
333
+ * Filters a host/operator discovery catalog down to the providers that satisfy
334
+ * the requested service-autodiscovery filter.
335
+ */
336
+ export function filterHostingOperatorDiscoveryCatalog(catalog, filter) {
337
+ return {
338
+ ...catalog,
339
+ providers: filterPublishedProvidersByDiscoveryFilter(catalog.providers, filter),
340
+ };
341
+ }
@@ -7,6 +7,7 @@ export * from './content';
7
7
  export * from './consent';
8
8
  export * from './did';
9
9
  export * from './did-resolution';
10
+ export * from './dataspace-discovery';
10
11
  export * from './didcomm';
11
12
  export * from './didcomm-submit';
12
13
  export * from './didcomm-submit-policy';
@@ -7,6 +7,7 @@ export * from './content.js';
7
7
  export * from './consent.js';
8
8
  export * from './did.js';
9
9
  export * from './did-resolution.js';
10
+ export * from './dataspace-discovery.js';
10
11
  export * from './didcomm.js';
11
12
  export * from './didcomm-submit.js';
12
13
  export * from './didcomm-submit-policy.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },