@twin.org/federated-catalogue-service 0.0.3-next.21 → 0.0.3-next.23

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.
@@ -1,3 +1,7 @@
1
+ // Copyright 2025 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { HttpContextIdKeys, HttpUrlHelper } from "@twin.org/api-models";
4
+ import { ContextIdStore } from "@twin.org/context";
1
5
  import { Coerce, ComponentFactory, Guards, Is } from "@twin.org/core";
2
6
  import { DataspaceProtocolCatalogTypes, DataspaceProtocolContexts } from "@twin.org/standards-dataspace-protocol";
3
7
  import { DcatClasses } from "@twin.org/standards-w3c-dcat";
@@ -247,7 +251,7 @@ export function generateRestRoutesFederatedCatalogue(baseRouteName, componentNam
247
251
  * @param httpRequestContext The request context for the operation.
248
252
  * @param componentName The name of the component to use.
249
253
  * @param request The request.
250
- * @returns The response.
254
+ * @returns A promise that resolves with the catalog response, including pagination headers and status code.
251
255
  */
252
256
  async function catalogRequest(httpRequestContext, componentName, request) {
253
257
  try {
@@ -255,12 +259,12 @@ async function catalogRequest(httpRequestContext, componentName, request) {
255
259
  Guards.object(ROUTES_SOURCE, "request.body", request.body);
256
260
  Guards.stringValue(ROUTES_SOURCE, "@type", request.body["@type"]);
257
261
  const trustPayload = HeaderHelper.extractBearer(request.headers?.[HeaderTypes.Authorization]);
258
- const hostingComponent = ComponentFactory.get(httpRequestContext.hostingComponentType ?? "hosting");
259
262
  const component = ComponentFactory.get(componentName);
260
263
  const result = await component.query(request.body.filter, request.query?.cursor, Coerce.integer(request.query?.limit), trustPayload);
261
264
  const headers = {};
262
265
  if (Is.stringValue(result.cursor)) {
263
- headers[HeaderTypes.Link] = HeaderHelper.createLinkHeader(await hostingComponent.buildPublicUrl(httpRequestContext.serverRequest.url), { cursor: result.cursor }, "next");
266
+ const contextIds = await ContextIdStore.getContextIds();
267
+ headers[HeaderTypes.Link] = HeaderHelper.createLinkHeader(HttpUrlHelper.replaceOrigin(httpRequestContext.serverRequest.url, contextIds?.[HttpContextIdKeys.PublicOrigin]), { cursor: result.cursor }, "next");
264
268
  }
265
269
  return {
266
270
  headers,
@@ -281,7 +285,7 @@ async function catalogRequest(httpRequestContext, componentName, request) {
281
285
  * @param httpRequestContext The request context for the operation.
282
286
  * @param componentName The name of the component to use.
283
287
  * @param request The request.
284
- * @returns The response.
288
+ * @returns A promise that resolves with the dataset response, containing the dataset or a CatalogError.
285
289
  */
286
290
  async function getDataset(httpRequestContext, componentName, request) {
287
291
  try {
@@ -309,7 +313,7 @@ async function getDataset(httpRequestContext, componentName, request) {
309
313
  * @param httpRequestContext The request context for the operation.
310
314
  * @param componentName The name of the component to use.
311
315
  * @param request The request.
312
- * @returns The response.
316
+ * @returns A promise that resolves with the set response, including a Location header on creation or a CatalogError on failure.
313
317
  */
314
318
  async function setDataset(httpRequestContext, componentName, request) {
315
319
  try {
@@ -344,7 +348,7 @@ async function setDataset(httpRequestContext, componentName, request) {
344
348
  * @param httpRequestContext The request context for the operation.
345
349
  * @param componentName The name of the component to use.
346
350
  * @param request The request.
347
- * @returns The response.
351
+ * @returns A promise that resolves with the remove response, containing no content on success or a CatalogError on failure.
348
352
  */
349
353
  async function removeDataset(httpRequestContext, componentName, request) {
350
354
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"federatedCatalogueRoutes.js","sourceRoot":"","sources":["../../src/federatedCatalogueRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIHostingComponent,\n\tIHttpRequestContext,\n\tIRestRoute,\n\tITag\n} from \"@twin.org/api-models\";\nimport { Coerce, ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport type {\n\tICatalogRequestRequest,\n\tICatalogRequestResponse,\n\tIFederatedCatalogueComponent,\n\tIDatasetGetRequest,\n\tIDatasetGetResponse,\n\tIDatasetRemoveRequest,\n\tIDatasetRemoveResponse,\n\tIDatasetSetRequest,\n\tIDatasetSetResponse\n} from \"@twin.org/federated-catalogue-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tDataspaceProtocolCatalogTypes,\n\tDataspaceProtocolContexts\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { DcatClasses, type DcatContextType } from \"@twin.org/standards-w3c-dcat\";\nimport { OdrlPolicyType } from \"@twin.org/standards-w3c-odrl\";\nimport { HeaderHelper, HeaderTypes, HttpStatusCode } from \"@twin.org/web\";\nimport { transformErrorToStatusCode, transformToCatalogError } from \"./utils/catalogErrorUtils.js\";\n\n/**\n * The source used when communicating about these routes.\n */\nconst ROUTES_SOURCE = \"federatedCatalogueRoutes\";\n\n/**\n * The tag to associate with the routes.\n */\nexport const tagsFederatedCatalogue: ITag[] = [\n\t{\n\t\tname: \"Federated Catalogue\",\n\t\tdescription:\n\t\t\t\"Service providing Dataspace Protocol-compliant catalogue endpoints for dataset discovery and query.\"\n\t}\n];\n\n/**\n * The REST routes for federated catalogue.\n * @param baseRouteName Prefix to prepend to the paths.\n * @param componentName The name of the component to use in the routes stored in the ComponentFactory.\n * @returns The generated routes.\n */\nexport function generateRestRoutesFederatedCatalogue(\n\tbaseRouteName: string,\n\tcomponentName: string\n): IRestRoute[] {\n\tconst catalogRequestRoute: IRestRoute<ICatalogRequestRequest, ICatalogRequestResponse> = {\n\t\toperationId: \"catalogRequest\",\n\t\tsummary: \"Query the federated catalogue for datasets\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/request`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tcatalogRequest(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ICatalogRequestRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"catalogRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t[HeaderTypes.Authorization]: \"Bearer <trust-token>\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogRequestMessage,\n\t\t\t\t\t\t\tfilter: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"catalogRequestNoFilterExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t[HeaderTypes.Authorization]: \"Bearer <trust-token>\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogRequestMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ICatalogRequestResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"catalogRequestResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\t\"@id\":\n\t\t\t\t\t\t\t\t\t\"urn:x-catalog:a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2\",\n\t\t\t\t\t\t\t\t\"@type\": \"Catalog\",\n\t\t\t\t\t\t\t\tparticipantId: \"did:example:node-identity-123\",\n\t\t\t\t\t\t\t\tdataset: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:dataset-123\",\n\t\t\t\t\t\t\t\t\t\t\"@type\": \"Dataset\",\n\t\t\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\t\t\t\"dcterms:description\": \"Historical energy consumption data\",\n\t\t\t\t\t\t\t\t\t\thasPolicy: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:policy-456\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"@type\": OdrlPolicyType.Offer,\n\t\t\t\t\t\t\t\t\t\t\t\tassigner: \"did:example:data-provider-789\"\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\tdistribution: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:distribution-789\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"@type\": \"Distribution\",\n\t\t\t\t\t\t\t\t\t\t\t\tformat: \"application/json\",\n\t\t\t\t\t\t\t\t\t\t\t\taccessService: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:access-service-321\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"@type\": \"DataService\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tendpointURL: \"https://example.com/data-access\"\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true,\n\t\tskipTenant: true\n\t};\n\n\tconst getDatasetRoute: IRestRoute<IDatasetGetRequest, IDatasetGetResponse> = {\n\t\toperationId: \"getDataset\",\n\t\tsummary: \"Retrieve a specific dataset by ID\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/datasets/:datasetId`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tgetDataset(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IDatasetGetRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"getDatasetRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t[HeaderTypes.Authorization]: \"Bearer <trust-token>\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tpathParams: {\n\t\t\t\t\t\t\tdatasetId: \"urn:uuid:dataset-123\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IDatasetGetResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"getDatasetResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\"@context\": DataspaceProtocolContexts.Context as unknown as DcatContextType,\n\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:dataset-123\",\n\t\t\t\t\t\t\t\t\"@type\": DcatClasses.Dataset,\n\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\t\"dcterms:description\": \"Historical energy consumption data\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true,\n\t\tskipTenant: true\n\t};\n\n\tconst setDatasetRoute: IRestRoute<IDatasetSetRequest, IDatasetSetResponse> = {\n\t\toperationId: \"setDataset\",\n\t\tsummary: \"Insert or update a dataset in the catalogue\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/datasets`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tsetDataset(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IDatasetSetRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"setDatasetRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t[HeaderTypes.Authorization]: \"Bearer <trust-token>\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\"@context\": DataspaceProtocolContexts.Context as unknown as DcatContextType,\n\t\t\t\t\t\t\t\"@id\": \"urn:uuid:dataset-123\",\n\t\t\t\t\t\t\t\"@type\": DcatClasses.Dataset,\n\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\"dcterms:description\": \"Historical energy consumption data\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IDatasetSetResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"setDatasetResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tstatusCode: HttpStatusCode.noContent\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true,\n\t\tskipTenant: true\n\t};\n\n\tconst removeDatasetRoute: IRestRoute<IDatasetRemoveRequest, IDatasetRemoveResponse> = {\n\t\toperationId: \"removeDataset\",\n\t\tsummary: \"Remove a dataset from the catalogue by ID\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"DELETE\",\n\t\tpath: `${baseRouteName}/datasets/:datasetId`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tremoveDataset(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IDatasetRemoveRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"removeDatasetRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t[HeaderTypes.Authorization]: \"Bearer <trust-token>\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tpathParams: {\n\t\t\t\t\t\t\tdatasetId: \"urn:uuid:dataset-123\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IDatasetRemoveResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"removeDatasetResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tstatusCode: HttpStatusCode.noContent\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true,\n\t\tskipTenant: true\n\t};\n\n\treturn [catalogRequestRoute, getDatasetRoute, setDatasetRoute, removeDatasetRoute];\n}\n\n/**\n * Handle the catalog request operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns The response.\n */\nasync function catalogRequest(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ICatalogRequestRequest\n): Promise<ICatalogRequestResponse> {\n\ttry {\n\t\tGuards.object<ICatalogRequestRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.body), request.body);\n\t\tGuards.stringValue(ROUTES_SOURCE, \"@type\", request.body[\"@type\"]);\n\n\t\tconst trustPayload = HeaderHelper.extractBearer(request.headers?.[HeaderTypes.Authorization]);\n\n\t\tconst hostingComponent = ComponentFactory.get<IHostingComponent>(\n\t\t\thttpRequestContext.hostingComponentType ?? \"hosting\"\n\t\t);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.query(\n\t\t\trequest.body.filter,\n\t\t\trequest.query?.cursor,\n\t\t\tCoerce.integer(request.query?.limit),\n\t\t\ttrustPayload\n\t\t);\n\n\t\tconst headers: ICatalogRequestResponse[\"headers\"] = {};\n\n\t\tif (Is.stringValue(result.cursor)) {\n\t\t\theaders[HeaderTypes.Link] = HeaderHelper.createLinkHeader(\n\t\t\t\tawait hostingComponent.buildPublicUrl(httpRequestContext.serverRequest.url),\n\t\t\t\t{ cursor: result.cursor },\n\t\t\t\t\"next\"\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\theaders,\n\t\t\tstatusCode: transformErrorToStatusCode(result.result),\n\t\t\tbody: result.result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n\n/**\n * Handle the get dataset operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns The response.\n */\nasync function getDataset(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IDatasetGetRequest\n): Promise<IDatasetGetResponse> {\n\ttry {\n\t\tGuards.object<IDatasetGetRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.pathParams), request.pathParams);\n\t\tGuards.stringValue(\n\t\t\tROUTES_SOURCE,\n\t\t\tnameof(request.pathParams.datasetId),\n\t\t\trequest.pathParams.datasetId\n\t\t);\n\n\t\tconst trustPayload = HeaderHelper.extractBearer(request.headers?.[HeaderTypes.Authorization]);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.get(request.pathParams.datasetId, trustPayload);\n\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(result),\n\t\t\tbody: result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n\n/**\n * Handle the set dataset operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns The response.\n */\nasync function setDataset(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IDatasetSetRequest\n): Promise<IDatasetSetResponse> {\n\ttry {\n\t\tGuards.object<IDatasetSetRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.body), request.body);\n\n\t\tconst trustPayload = HeaderHelper.extractBearer(request.headers?.[HeaderTypes.Authorization]);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.set(request.body, trustPayload);\n\n\t\tif (Is.stringValue(result)) {\n\t\t\treturn {\n\t\t\t\tstatusCode: HttpStatusCode.created,\n\t\t\t\theaders: {\n\t\t\t\t\t[HeaderTypes.Location]: Coerce.string(result)\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(result),\n\t\t\tbody: result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n\n/**\n * Handle the remove dataset operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns The response.\n */\nasync function removeDataset(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IDatasetRemoveRequest\n): Promise<IDatasetRemoveResponse> {\n\ttry {\n\t\tGuards.object<IDatasetRemoveRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.pathParams), request.pathParams);\n\t\tGuards.stringValue(\n\t\t\tROUTES_SOURCE,\n\t\t\tnameof(request.pathParams.datasetId),\n\t\t\trequest.pathParams.datasetId\n\t\t);\n\n\t\tconst trustPayload = HeaderHelper.extractBearer(request.headers?.[HeaderTypes.Authorization]);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.remove(request.pathParams.datasetId, trustPayload);\n\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(result) ?? HttpStatusCode.noContent,\n\t\t\tbody: result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"federatedCatalogueRoutes.js","sourceRoot":"","sources":["../../src/federatedCatalogueRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpContextIdKeys,\n\tHttpUrlHelper,\n\ttype IHttpRequestContext,\n\ttype IRestRoute,\n\ttype ITag\n} from \"@twin.org/api-models\";\nimport { ContextIdStore } from \"@twin.org/context\";\nimport { Coerce, ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport type {\n\tICatalogRequestRequest,\n\tICatalogRequestResponse,\n\tIDatasetGetRequest,\n\tIDatasetGetResponse,\n\tIDatasetRemoveRequest,\n\tIDatasetRemoveResponse,\n\tIDatasetSetRequest,\n\tIDatasetSetResponse,\n\tIFederatedCatalogueComponent\n} from \"@twin.org/federated-catalogue-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tDataspaceProtocolCatalogTypes,\n\tDataspaceProtocolContexts\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { DcatClasses, type DcatContextType } from \"@twin.org/standards-w3c-dcat\";\nimport { OdrlPolicyType } from \"@twin.org/standards-w3c-odrl\";\nimport { HeaderHelper, HeaderTypes, HttpStatusCode } from \"@twin.org/web\";\nimport { transformErrorToStatusCode, transformToCatalogError } from \"./utils/catalogErrorUtils.js\";\n\n/**\n * The source used when communicating about these routes.\n */\nconst ROUTES_SOURCE = \"federatedCatalogueRoutes\";\n\n/**\n * The tag to associate with the routes.\n */\nexport const tagsFederatedCatalogue: ITag[] = [\n\t{\n\t\tname: \"Federated Catalogue\",\n\t\tdescription:\n\t\t\t\"Service providing Dataspace Protocol-compliant catalogue endpoints for dataset discovery and query.\"\n\t}\n];\n\n/**\n * The REST routes for federated catalogue.\n * @param baseRouteName Prefix to prepend to the paths.\n * @param componentName The name of the component to use in the routes stored in the ComponentFactory.\n * @returns The generated routes.\n */\nexport function generateRestRoutesFederatedCatalogue(\n\tbaseRouteName: string,\n\tcomponentName: string\n): IRestRoute[] {\n\tconst catalogRequestRoute: IRestRoute<ICatalogRequestRequest, ICatalogRequestResponse> = {\n\t\toperationId: \"catalogRequest\",\n\t\tsummary: \"Query the federated catalogue for datasets\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/request`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tcatalogRequest(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ICatalogRequestRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"catalogRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t[HeaderTypes.Authorization]: \"Bearer <trust-token>\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogRequestMessage,\n\t\t\t\t\t\t\tfilter: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"catalogRequestNoFilterExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t[HeaderTypes.Authorization]: \"Bearer <trust-token>\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogRequestMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ICatalogRequestResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"catalogRequestResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\t\"@id\":\n\t\t\t\t\t\t\t\t\t\"urn:x-catalog:a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2\",\n\t\t\t\t\t\t\t\t\"@type\": \"Catalog\",\n\t\t\t\t\t\t\t\tparticipantId: \"did:example:node-identity-123\",\n\t\t\t\t\t\t\t\tdataset: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:dataset-123\",\n\t\t\t\t\t\t\t\t\t\t\"@type\": \"Dataset\",\n\t\t\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\t\t\t\"dcterms:description\": \"Historical energy consumption data\",\n\t\t\t\t\t\t\t\t\t\thasPolicy: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:policy-456\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"@type\": OdrlPolicyType.Offer,\n\t\t\t\t\t\t\t\t\t\t\t\tassigner: \"did:example:data-provider-789\"\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\tdistribution: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:distribution-789\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"@type\": \"Distribution\",\n\t\t\t\t\t\t\t\t\t\t\t\tformat: \"application/json\",\n\t\t\t\t\t\t\t\t\t\t\t\taccessService: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:access-service-321\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"@type\": \"DataService\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tendpointURL: \"https://example.com/data-access\"\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true,\n\t\tskipTenant: true\n\t};\n\n\tconst getDatasetRoute: IRestRoute<IDatasetGetRequest, IDatasetGetResponse> = {\n\t\toperationId: \"getDataset\",\n\t\tsummary: \"Retrieve a specific dataset by ID\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/datasets/:datasetId`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tgetDataset(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IDatasetGetRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"getDatasetRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t[HeaderTypes.Authorization]: \"Bearer <trust-token>\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tpathParams: {\n\t\t\t\t\t\t\tdatasetId: \"urn:uuid:dataset-123\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IDatasetGetResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"getDatasetResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\"@context\": DataspaceProtocolContexts.Context as unknown as DcatContextType,\n\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:dataset-123\",\n\t\t\t\t\t\t\t\t\"@type\": DcatClasses.Dataset,\n\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\t\"dcterms:description\": \"Historical energy consumption data\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true,\n\t\tskipTenant: true\n\t};\n\n\tconst setDatasetRoute: IRestRoute<IDatasetSetRequest, IDatasetSetResponse> = {\n\t\toperationId: \"setDataset\",\n\t\tsummary: \"Insert or update a dataset in the catalogue\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/datasets`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tsetDataset(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IDatasetSetRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"setDatasetRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t[HeaderTypes.Authorization]: \"Bearer <trust-token>\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\"@context\": DataspaceProtocolContexts.Context as unknown as DcatContextType,\n\t\t\t\t\t\t\t\"@id\": \"urn:uuid:dataset-123\",\n\t\t\t\t\t\t\t\"@type\": DcatClasses.Dataset,\n\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\"dcterms:description\": \"Historical energy consumption data\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IDatasetSetResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"setDatasetResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tstatusCode: HttpStatusCode.noContent\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true,\n\t\tskipTenant: true\n\t};\n\n\tconst removeDatasetRoute: IRestRoute<IDatasetRemoveRequest, IDatasetRemoveResponse> = {\n\t\toperationId: \"removeDataset\",\n\t\tsummary: \"Remove a dataset from the catalogue by ID\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"DELETE\",\n\t\tpath: `${baseRouteName}/datasets/:datasetId`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tremoveDataset(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IDatasetRemoveRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"removeDatasetRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t[HeaderTypes.Authorization]: \"Bearer <trust-token>\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tpathParams: {\n\t\t\t\t\t\t\tdatasetId: \"urn:uuid:dataset-123\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IDatasetRemoveResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"removeDatasetResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tstatusCode: HttpStatusCode.noContent\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\tskipAuth: true,\n\t\tskipTenant: true\n\t};\n\n\treturn [catalogRequestRoute, getDatasetRoute, setDatasetRoute, removeDatasetRoute];\n}\n\n/**\n * Handle the catalog request operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns A promise that resolves with the catalog response, including pagination headers and status code.\n */\nasync function catalogRequest(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ICatalogRequestRequest\n): Promise<ICatalogRequestResponse> {\n\ttry {\n\t\tGuards.object<ICatalogRequestRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.body), request.body);\n\t\tGuards.stringValue(ROUTES_SOURCE, \"@type\", request.body[\"@type\"]);\n\n\t\tconst trustPayload = HeaderHelper.extractBearer(request.headers?.[HeaderTypes.Authorization]);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.query(\n\t\t\trequest.body.filter,\n\t\t\trequest.query?.cursor,\n\t\t\tCoerce.integer(request.query?.limit),\n\t\t\ttrustPayload\n\t\t);\n\n\t\tconst headers: ICatalogRequestResponse[\"headers\"] = {};\n\n\t\tif (Is.stringValue(result.cursor)) {\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\theaders[HeaderTypes.Link] = HeaderHelper.createLinkHeader(\n\t\t\t\tHttpUrlHelper.replaceOrigin(\n\t\t\t\t\thttpRequestContext.serverRequest.url,\n\t\t\t\t\tcontextIds?.[HttpContextIdKeys.PublicOrigin]\n\t\t\t\t),\n\t\t\t\t{ cursor: result.cursor },\n\t\t\t\t\"next\"\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\theaders,\n\t\t\tstatusCode: transformErrorToStatusCode(result.result),\n\t\t\tbody: result.result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n\n/**\n * Handle the get dataset operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns A promise that resolves with the dataset response, containing the dataset or a CatalogError.\n */\nasync function getDataset(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IDatasetGetRequest\n): Promise<IDatasetGetResponse> {\n\ttry {\n\t\tGuards.object<IDatasetGetRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.pathParams), request.pathParams);\n\t\tGuards.stringValue(\n\t\t\tROUTES_SOURCE,\n\t\t\tnameof(request.pathParams.datasetId),\n\t\t\trequest.pathParams.datasetId\n\t\t);\n\n\t\tconst trustPayload = HeaderHelper.extractBearer(request.headers?.[HeaderTypes.Authorization]);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.get(request.pathParams.datasetId, trustPayload);\n\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(result),\n\t\t\tbody: result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n\n/**\n * Handle the set dataset operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns A promise that resolves with the set response, including a Location header on creation or a CatalogError on failure.\n */\nasync function setDataset(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IDatasetSetRequest\n): Promise<IDatasetSetResponse> {\n\ttry {\n\t\tGuards.object<IDatasetSetRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.body), request.body);\n\n\t\tconst trustPayload = HeaderHelper.extractBearer(request.headers?.[HeaderTypes.Authorization]);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.set(request.body, trustPayload);\n\n\t\tif (Is.stringValue(result)) {\n\t\t\treturn {\n\t\t\t\tstatusCode: HttpStatusCode.created,\n\t\t\t\theaders: {\n\t\t\t\t\t[HeaderTypes.Location]: Coerce.string(result)\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(result),\n\t\t\tbody: result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n\n/**\n * Handle the remove dataset operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns A promise that resolves with the remove response, containing no content on success or a CatalogError on failure.\n */\nasync function removeDataset(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IDatasetRemoveRequest\n): Promise<IDatasetRemoveResponse> {\n\ttry {\n\t\tGuards.object<IDatasetRemoveRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.pathParams), request.pathParams);\n\t\tGuards.stringValue(\n\t\t\tROUTES_SOURCE,\n\t\t\tnameof(request.pathParams.datasetId),\n\t\t\trequest.pathParams.datasetId\n\t\t);\n\n\t\tconst trustPayload = HeaderHelper.extractBearer(request.headers?.[HeaderTypes.Authorization]);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.remove(request.pathParams.datasetId, trustPayload);\n\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(result) ?? HttpStatusCode.noContent,\n\t\t\tbody: result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IFederatedCatalogueServiceConfig.js","sourceRoot":"","sources":["../../../src/models/IFederatedCatalogueServiceConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the FederatedCatalogueService.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface IFederatedCatalogueServiceConfig {}\n"]}
1
+ {"version":3,"file":"IFederatedCatalogueServiceConfig.js","sourceRoot":"","sources":["../../../src/models/IFederatedCatalogueServiceConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the FederatedCatalogueService.\n */\nexport interface IFederatedCatalogueServiceConfig {\n\t/**\n\t * Timeout in milliseconds for acquiring a mutex lock, defaults to 5000ms.\n\t */\n\tmutexTimeoutMs?: number;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IFederatedCatalogueServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IFederatedCatalogueServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IFederatedCatalogueServiceConfig } from \"./IFederatedCatalogueServiceConfig.js\";\n\n/**\n * Options for the FederatedCatalogueService constructor.\n */\nexport interface IFederatedCatalogueServiceConstructorOptions {\n\t/**\n\t * The entity storage for datasets.\n\t * @default dataset\n\t */\n\tdatasetEntityStorageType?: string;\n\n\t/**\n\t * The logging component for the service.\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * Trust component type for trust verification.\n\t * @default trust\n\t */\n\ttrustComponentType?: string;\n\n\t/**\n\t * Configuration for the federated catalogue service.\n\t */\n\tconfig?: IFederatedCatalogueServiceConfig;\n}\n"]}
1
+ {"version":3,"file":"IFederatedCatalogueServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IFederatedCatalogueServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IFederatedCatalogueServiceConfig } from \"./IFederatedCatalogueServiceConfig.js\";\n\n/**\n * Options for the FederatedCatalogueService constructor.\n */\nexport interface IFederatedCatalogueServiceConstructorOptions {\n\t/**\n\t * The entity storage for datasets.\n\t * @default dataset\n\t */\n\tdatasetEntityStorageType?: string;\n\n\t/**\n\t * The logging component for the service.\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * Trust component type for trust verification.\n\t * @default trust\n\t */\n\ttrustComponentType?: string;\n\n\t/**\n\t * The component type for the optional telemetry component used for event metrics, defaults to no telemetry.\n\t */\n\ttelemetryComponentType?: string;\n\n\t/**\n\t * Configuration for the federated catalogue service.\n\t */\n\tconfig?: IFederatedCatalogueServiceConfig;\n}\n"]}
@@ -1,4 +1,7 @@
1
1
  import { generateRestRoutesFederatedCatalogue, tagsFederatedCatalogue } from "./federatedCatalogueRoutes.js";
2
+ /**
3
+ * REST route entry points for the federated catalogue service.
4
+ */
2
5
  export const restEntryPoints = [
3
6
  {
4
7
  name: "federated-catalogue",
@@ -1 +1 @@
1
- {"version":3,"file":"restEntryPoints.js","sourceRoot":"","sources":["../../src/restEntryPoints.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,oCAAoC,EACpC,sBAAsB,EACtB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,CAAC,MAAM,eAAe,GAA2B;IACtD;QACC,IAAI,EAAE,qBAAqB;QAC3B,gBAAgB,EAAE,SAAS;QAC3B,IAAI,EAAE,sBAAsB;QAC5B,cAAc,EAAE,oCAAoC;KACpD;CACD,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IRestRouteEntryPoint } from \"@twin.org/api-models\";\nimport {\n\tgenerateRestRoutesFederatedCatalogue,\n\ttagsFederatedCatalogue\n} from \"./federatedCatalogueRoutes.js\";\n\nexport const restEntryPoints: IRestRouteEntryPoint[] = [\n\t{\n\t\tname: \"federated-catalogue\",\n\t\tdefaultBaseRoute: \"catalog\",\n\t\ttags: tagsFederatedCatalogue,\n\t\tgenerateRoutes: generateRestRoutesFederatedCatalogue\n\t}\n];\n"]}
1
+ {"version":3,"file":"restEntryPoints.js","sourceRoot":"","sources":["../../src/restEntryPoints.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,oCAAoC,EACpC,sBAAsB,EACtB,MAAM,+BAA+B,CAAC;AAEvC;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAA2B;IACtD;QACC,IAAI,EAAE,qBAAqB;QAC3B,gBAAgB,EAAE,SAAS;QAC3B,IAAI,EAAE,sBAAsB;QAC5B,cAAc,EAAE,oCAAoC;KACpD;CACD,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IRestRouteEntryPoint } from \"@twin.org/api-models\";\nimport {\n\tgenerateRestRoutesFederatedCatalogue,\n\ttagsFederatedCatalogue\n} from \"./federatedCatalogueRoutes.js\";\n\n/**\n * REST route entry points for the federated catalogue service.\n */\nexport const restEntryPoints: IRestRouteEntryPoint[] = [\n\t{\n\t\tname: \"federated-catalogue\",\n\t\tdefaultBaseRoute: \"catalog\",\n\t\ttags: tagsFederatedCatalogue,\n\t\tgenerateRoutes: generateRestRoutesFederatedCatalogue\n\t}\n];\n"]}
@@ -2,16 +2,17 @@
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  import { HttpUrlHelper } from "@twin.org/api-models";
4
4
  import { ContextIdKeys } from "@twin.org/context";
5
- import { ArrayHelper, BaseError, ComponentFactory, Converter, GeneralError, Guards, Is, JsonHelper, Mutex, NotFoundError, ObjectHelper, Url, Urn, Validation } from "@twin.org/core";
5
+ import { ArrayHelper, BaseError, Coerce, ComponentFactory, Converter, GeneralError, Guards, Is, JsonHelper, Mutex, NotFoundError, ObjectHelper, Url, Urn, Validation } from "@twin.org/core";
6
6
  import { Blake2b } from "@twin.org/crypto";
7
7
  import { JsonLdHelper, JsonLdProcessor } from "@twin.org/data-json-ld";
8
8
  import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
9
- import { FederatedCatalogueFilterFactory } from "@twin.org/federated-catalogue-models";
9
+ import { FederatedCatalogueFilterFactory, FederatedCatalogueMetricIds, FederatedCatalogueMetrics } from "@twin.org/federated-catalogue-models";
10
10
  import { DataspaceProtocolCatalogTypes, DataspaceProtocolContexts, DataspaceProtocolDataTypes, DataspaceProtocolHelper } from "@twin.org/standards-dataspace-protocol";
11
11
  import { DublinCoreContexts, DublinCoreDataTypes } from "@twin.org/standards-dublin-core";
12
12
  import { FoafDataTypes } from "@twin.org/standards-foaf";
13
13
  import { DcatContexts, DcatDataTypes } from "@twin.org/standards-w3c-dcat";
14
14
  import { OdrlContexts } from "@twin.org/standards-w3c-odrl";
15
+ import { MetricHelper } from "@twin.org/telemetry-models";
15
16
  import { TrustHelper } from "@twin.org/trust-models";
16
17
  import { transformToCatalogError } from "../utils/catalogErrorUtils.js";
17
18
  import { datasetEntityToModel, datasetModelToEntity } from "../utils/datasetConverters.js";
@@ -39,6 +40,16 @@ export class FederatedCatalogueService {
39
40
  * @internal
40
41
  */
41
42
  _trustComponent;
43
+ /**
44
+ * The optional telemetry component for event metrics.
45
+ * @internal
46
+ */
47
+ _telemetryComponent;
48
+ /**
49
+ * Timeout in milliseconds for acquiring a mutex lock.
50
+ * @internal
51
+ */
52
+ _mutexTimeoutMs;
42
53
  /**
43
54
  * Create a new instance of FederatedCatalogueService.
44
55
  * @param options The options for the service.
@@ -47,6 +58,8 @@ export class FederatedCatalogueService {
47
58
  this._logging = ComponentFactory.getIfExists(options?.loggingComponentType);
48
59
  this._datasetStorage = EntityStorageConnectorFactory.get(options?.datasetEntityStorageType ?? "dataset");
49
60
  this._trustComponent = ComponentFactory.get(options?.trustComponentType ?? "trust");
61
+ this._telemetryComponent = ComponentFactory.getIfExists(options?.telemetryComponentType);
62
+ this._mutexTimeoutMs = Coerce.integer(options?.config?.mutexTimeoutMs);
50
63
  // Register JSON-LD redirects for offline processing
51
64
  DcatDataTypes.registerRedirects();
52
65
  DublinCoreDataTypes.registerRedirects();
@@ -62,11 +75,18 @@ export class FederatedCatalogueService {
62
75
  className() {
63
76
  return FederatedCatalogueService.CLASS_NAME;
64
77
  }
78
+ /**
79
+ * Register all federated catalogue metrics with the telemetry component.
80
+ * @returns A promise that resolves when all metrics have been registered.
81
+ */
82
+ async start() {
83
+ await MetricHelper.createMetrics(this._telemetryComponent, FederatedCatalogueMetrics);
84
+ }
65
85
  /**
66
86
  * Retrieve a dataset by its unique identifier.
67
87
  * @param datasetId The unique identifier of the dataset.
68
88
  * @param trustPayload Optional payload for trust evaluation, if applicable.
69
- * @returns The dataset if found, or a CatalogError if not found or an error occurs.
89
+ * @returns A promise that resolves with the dataset if found, or a CatalogError if not found or an error occurs.
70
90
  */
71
91
  async get(datasetId, trustPayload) {
72
92
  try {
@@ -88,6 +108,7 @@ export class FederatedCatalogueService {
88
108
  // This ensures the payload matches exactly what the DS Protocol mandates
89
109
  const normalizedDataset = await DataspaceProtocolHelper.normalize(JsonLdHelper.toNodeObject(dataset));
90
110
  const structured = JsonLdHelper.toStructuredObject(normalizedDataset);
111
+ await MetricHelper.metricIncrement(this._telemetryComponent, FederatedCatalogueMetricIds.DatasetsRetrieved);
91
112
  return structured;
92
113
  }
93
114
  catch (error) {
@@ -98,7 +119,7 @@ export class FederatedCatalogueService {
98
119
  * Insert or update a dataset in the catalogue.
99
120
  * @param dataset The dataset to store.
100
121
  * @param trustPayload Optional payload for trust evaluation, if applicable.
101
- * @returns The unique identifier of the stored dataset, or a CatalogError if an error occurs.
122
+ * @returns A promise that resolves with the unique identifier of the stored dataset, or a CatalogError if an error occurs.
102
123
  */
103
124
  async set(dataset, trustPayload) {
104
125
  try {
@@ -145,7 +166,7 @@ export class FederatedCatalogueService {
145
166
  const datasetEntity = datasetModelToEntity(normalizedDataset, trustInfo.identity);
146
167
  // Serialise the read-check-write cycle so concurrent requests for the
147
168
  // same dataset cannot both pass the ownership check and overwrite each other.
148
- await Mutex.lock(datasetId);
169
+ await Mutex.lock(datasetId, { throwOnTimeout: true, timeoutMs: this._mutexTimeoutMs });
149
170
  try {
150
171
  const existingEntity = await this._datasetStorage.get(datasetId);
151
172
  if (!Is.empty(existingEntity) && existingEntity.ownerId !== trustInfo.identity) {
@@ -174,6 +195,7 @@ export class FederatedCatalogueService {
174
195
  message: "filterIndexPersisted",
175
196
  data: { datasetId, filterType, indexCount: Object.keys(filterIndexes).length }
176
197
  });
198
+ await MetricHelper.metricIncrement(this._telemetryComponent, FederatedCatalogueMetricIds.FilterIndexesCreated, { filterType, indexCount: Object.keys(filterIndexes).length });
177
199
  }
178
200
  catch (error) {
179
201
  await this._logging?.log({
@@ -184,9 +206,11 @@ export class FederatedCatalogueService {
184
206
  data: { datasetId, filterType },
185
207
  error: BaseError.fromError(error)
186
208
  });
209
+ await MetricHelper.metricIncrement(this._telemetryComponent, FederatedCatalogueMetricIds.FilterIndexFailures, { filterType });
187
210
  }
188
211
  }
189
212
  await this._datasetStorage.set(datasetEntity);
213
+ await MetricHelper.metricIncrement(this._telemetryComponent, FederatedCatalogueMetricIds.DatasetsStored);
190
214
  return datasetId;
191
215
  }
192
216
  finally {
@@ -202,13 +226,13 @@ export class FederatedCatalogueService {
202
226
  * Indexes are automatically removed as they are stored with the dataset.
203
227
  * @param datasetId The unique identifier of the dataset to remove.
204
228
  * @param trustPayload Optional payload for trust evaluation, if applicable.
205
- * @returns Nothing, or a CatalogError if an error occurs.
229
+ * @returns A promise that resolves with undefined on success, or a CatalogError if removal fails.
206
230
  */
207
231
  async remove(datasetId, trustPayload) {
208
232
  try {
209
233
  Guards.stringValue(FederatedCatalogueService.CLASS_NAME, "datasetId", datasetId);
210
234
  const trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, "remove");
211
- await Mutex.lock(datasetId);
235
+ await Mutex.lock(datasetId, { throwOnTimeout: true, timeoutMs: this._mutexTimeoutMs });
212
236
  try {
213
237
  const existingEntity = await this._datasetStorage.get(datasetId);
214
238
  if (!Is.empty(existingEntity) && existingEntity.ownerId !== trustInfo.identity) {
@@ -224,6 +248,7 @@ export class FederatedCatalogueService {
224
248
  data: { datasetId }
225
249
  });
226
250
  await this._datasetStorage.remove(datasetId);
251
+ await MetricHelper.metricIncrement(this._telemetryComponent, FederatedCatalogueMetricIds.DatasetsRemoved);
227
252
  }
228
253
  finally {
229
254
  Mutex.unlock(datasetId);
@@ -235,21 +260,20 @@ export class FederatedCatalogueService {
235
260
  }
236
261
  /**
237
262
  * Execute a query against the catalogue using registered filter plugins.
238
- * Returns a DS Protocol compliant Catalog object with participantId.
263
+ * Returns a Dataspace Protocol compliant Catalog object with participantId.
239
264
  *
240
265
  * The root catalog's participantId is the requesting participant (from context).
241
266
  * Own datasets (matching requestingParticipantId) go directly in root dataset[].
242
267
  * Other participants' datasets are grouped in nested catalog[] entries.
243
268
  *
244
269
  * For anonymous requests (no context), uses the first publisher found as fallback.
245
- * Returns CatalogError 404 when no datasets exist, CatalogError 400 for invalid requests.
270
+ * Returns a CatalogError with status 404 when no datasets exist, or status 400 for invalid requests.
246
271
  *
247
- * @param filter The filter criteria containing @type, optional cursor and limit properties.
272
+ * @param filter The filter criteria array, where the first element contains @type.
248
273
  * @param cursor Optional cursor for pagination.
249
274
  * @param limit Optional limit for pagination.
250
275
  * @param trustPayload Optional payload for trust evaluation, if applicable.
251
- * @returns Complete IDataspaceProtocolCatalog with @context, @id, @type, participantId, dataset/catalog,
252
- * or CatalogError if validation fails or an error occurs.
276
+ * @returns A promise that resolves with the catalog result and optional next-page cursor.
253
277
  */
254
278
  async query(filter, cursor, limit, trustPayload) {
255
279
  try {
@@ -291,6 +315,7 @@ export class FederatedCatalogueService {
291
315
  message: "catalogQueryComplete",
292
316
  data: { resultCount: datasets.length, hasMore: Is.stringValue(resultCursor) }
293
317
  });
318
+ await MetricHelper.metricIncrement(this._telemetryComponent, FederatedCatalogueMetricIds.QueriesExecuted, { resultCount: datasets.length, hasMore: Is.stringValue(resultCursor) });
294
319
  // Return CatalogError 404 when no datasets exist
295
320
  if (!Is.arrayValue(datasets)) {
296
321
  throw new NotFoundError(FederatedCatalogueService.CLASS_NAME, "noDatasetsFound");
@@ -386,6 +411,7 @@ export class FederatedCatalogueService {
386
411
  * Bake the publishing organization token into each distribution's accessService URL.
387
412
  * @param entity The dataset entity to modify in place.
388
413
  * @param organizationId The publishing organization id to bake.
414
+ * @returns A promise that resolves when all distribution URLs have been updated.
389
415
  * @internal
390
416
  */
391
417
  async bakeOrganizationIntoDistributions(entity, organizationId) {
@@ -1 +1 @@
1
- {"version":3,"file":"federatedCatalogueService.js","sourceRoot":"","sources":["../../../src/services/federatedCatalogueService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { HttpUrlHelper } from \"@twin.org/api-models\";\nimport { ContextIdKeys } from \"@twin.org/context\";\nimport {\n\tArrayHelper,\n\tBaseError,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tJsonHelper,\n\tMutex,\n\tNotFoundError,\n\tObjectHelper,\n\tUrl,\n\tUrn,\n\tValidation\n} from \"@twin.org/core\";\nimport { Blake2b } from \"@twin.org/crypto\";\nimport { JsonLdHelper, JsonLdProcessor } from \"@twin.org/data-json-ld\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport {\n\tFederatedCatalogueFilterFactory,\n\ttype IFederatedCatalogueComponent\n} from \"@twin.org/federated-catalogue-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tDataspaceProtocolCatalogTypes,\n\tDataspaceProtocolContexts,\n\tDataspaceProtocolDataTypes,\n\tDataspaceProtocolHelper,\n\ttype IDataspaceProtocolCatalog,\n\ttype IDataspaceProtocolCatalogError\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { DublinCoreContexts, DublinCoreDataTypes } from \"@twin.org/standards-dublin-core\";\nimport { FoafDataTypes } from \"@twin.org/standards-foaf\";\nimport {\n\tDcatContexts,\n\tDcatDataTypes,\n\ttype DcatContextType,\n\ttype IDcatDataset\n} from \"@twin.org/standards-w3c-dcat\";\nimport { OdrlContexts } from \"@twin.org/standards-w3c-odrl\";\nimport { TrustHelper, type ITrustComponent } from \"@twin.org/trust-models\";\nimport type { Dataset } from \"../entities/dataset.js\";\nimport type { IFederatedCatalogueServiceConstructorOptions } from \"../models/IFederatedCatalogueServiceConstructorOptions.js\";\nimport { transformToCatalogError } from \"../utils/catalogErrorUtils.js\";\nimport { datasetEntityToModel, datasetModelToEntity } from \"../utils/datasetConverters.js\";\n\n/**\n * Service for managing federated catalogue operations.\n * Provides Dataspace Protocol-compliant catalog endpoints for dataset registry and query.\n */\nexport class FederatedCatalogueService implements IFederatedCatalogueComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<FederatedCatalogueService>();\n\n\t/**\n\t * The logging component for the federated catalogue service.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The entity storage connector for datasets.\n\t * @internal\n\t */\n\tprivate readonly _datasetStorage: IEntityStorageConnector<Dataset>;\n\n\t/**\n\t * The trust component for token verification and generation.\n\t * @internal\n\t */\n\tprivate readonly _trustComponent: ITrustComponent;\n\n\t/**\n\t * Create a new instance of FederatedCatalogueService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IFederatedCatalogueServiceConstructorOptions) {\n\t\tthis._logging = ComponentFactory.getIfExists<ILoggingComponent>(options?.loggingComponentType);\n\n\t\tthis._datasetStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.datasetEntityStorageType ?? \"dataset\"\n\t\t);\n\n\t\tthis._trustComponent = ComponentFactory.get<ITrustComponent>(\n\t\t\toptions?.trustComponentType ?? \"trust\"\n\t\t);\n\n\t\t// Register JSON-LD redirects for offline processing\n\t\tDcatDataTypes.registerRedirects();\n\t\tDublinCoreDataTypes.registerRedirects();\n\t\tFoafDataTypes.registerRedirects();\n\t\tDataspaceProtocolDataTypes.registerRedirects();\n\n\t\t// Register DS Protocol data types for conformance checking\n\t\tDataspaceProtocolDataTypes.registerTypes();\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn FederatedCatalogueService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Retrieve a dataset by its unique identifier.\n\t * @param datasetId The unique identifier of the dataset.\n\t * @param trustPayload Optional payload for trust evaluation, if applicable.\n\t * @returns The dataset if found, or a CatalogError if not found or an error occurs.\n\t */\n\tpublic async get(\n\t\tdatasetId: string,\n\t\ttrustPayload: unknown\n\t): Promise<IDcatDataset | IDataspaceProtocolCatalogError> {\n\t\ttry {\n\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(datasetId), datasetId);\n\t\t\tawait TrustHelper.verifyTrust(this._trustComponent, trustPayload, \"get\");\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"datasetRetrieve\",\n\t\t\t\tdata: { datasetId }\n\t\t\t});\n\n\t\t\tconst datasetEntity = await this._datasetStorage.get(datasetId);\n\n\t\t\tif (!datasetEntity) {\n\t\t\t\tthrow new NotFoundError(FederatedCatalogueService.CLASS_NAME, \"datasetNotFound\", datasetId);\n\t\t\t}\n\n\t\t\tconst dataset = datasetEntityToModel(datasetEntity);\n\n\t\t\t// Normalize to DS Protocol compliant format\n\t\t\t// This ensures the payload matches exactly what the DS Protocol mandates\n\t\t\tconst normalizedDataset = await DataspaceProtocolHelper.normalize(\n\t\t\t\tJsonLdHelper.toNodeObject(dataset)\n\t\t\t);\n\n\t\t\tconst structured = JsonLdHelper.toStructuredObject<IDcatDataset>(normalizedDataset);\n\t\t\treturn structured;\n\t\t} catch (error) {\n\t\t\treturn transformToCatalogError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Insert or update a dataset in the catalogue.\n\t * @param dataset The dataset to store.\n\t * @param trustPayload Optional payload for trust evaluation, if applicable.\n\t * @returns The unique identifier of the stored dataset, or a CatalogError if an error occurs.\n\t */\n\tpublic async set(\n\t\tdataset: IDcatDataset,\n\t\ttrustPayload: unknown\n\t): Promise<string | IDataspaceProtocolCatalogError> {\n\t\ttry {\n\t\t\tGuards.object(FederatedCatalogueService.CLASS_NAME, nameof(dataset), dataset);\n\n\t\t\tconst trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, \"set\");\n\n\t\t\t// Normalize @id from dcterms:identifier if provided\n\t\t\tconst datasetId = dataset[\"@id\"] ?? dataset[\"dcterms:identifier\"];\n\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(datasetId), datasetId);\n\n\t\t\t// Set @id if it was derived from dcterms:identifier\n\t\t\tif (Is.empty(dataset[\"@id\"]) && !Is.empty(dataset[\"dcterms:identifier\"])) {\n\t\t\t\tdataset[\"@id\"] = datasetId;\n\t\t\t}\n\n\t\t\t// Validate @id is a valid URI (URN or URL) per DS Protocol\n\t\t\tconst isValidUrn = !Is.empty(Urn.tryParseExact(datasetId));\n\t\t\tconst isValidUrl = !Is.empty(Url.tryParseExact(datasetId));\n\t\t\tif (!isValidUrn && !isValidUrl) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetIdInvalidUri\", {\n\t\t\t\t\tdatasetId\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Validate @type exists\n\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, \"@type\", dataset[\"@type\"]);\n\n\t\t\t// Validate dcterms:publisher exists (required for multi-participant catalog)\n\t\t\t// The publisher is used to derive participantId when returning catalog query results\n\t\t\tconst publisher = dataset[\"dcterms:publisher\"];\n\t\t\tif (Is.empty(publisher)) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetMissingPublisher\", {\n\t\t\t\t\tdatasetId\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// DS Protocol compliance validation\n\t\t\tconst validationFailures = await DataspaceProtocolHelper.validate(\n\t\t\t\tJsonLdHelper.toNodeObject(dataset)\n\t\t\t);\n\n\t\t\tValidation.asValidationError(\n\t\t\t\tFederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\"dataset\",\n\t\t\t\tvalidationFailures\n\t\t\t);\n\n\t\t\t// Normalize dataset for storage using JSON-LD compaction\n\t\t\t// This ensures the dataset uses prefixed properties that entity storage expects\n\t\t\t// Entity storage schema uses DCAT-prefixed properties (dcat:distribution, not distribution)\n\t\t\t// Use a standard context with prefixes to ensure proper normalization\n\t\t\tconst storageContext: DcatContextType = {\n\t\t\t\tdcat: DcatContexts.Namespace,\n\t\t\t\tdcterms: DublinCoreContexts.NamespaceTerms,\n\t\t\t\todrl: OdrlContexts.Namespace\n\t\t\t};\n\t\t\tconst normalizedDataset = await JsonLdProcessor.compact(dataset, storageContext);\n\n\t\t\tconst datasetEntity = datasetModelToEntity(normalizedDataset, trustInfo.identity);\n\n\t\t\t// Serialise the read-check-write cycle so concurrent requests for the\n\t\t\t// same dataset cannot both pass the ownership check and overwrite each other.\n\t\t\tawait Mutex.lock(datasetId);\n\t\t\ttry {\n\t\t\t\tconst existingEntity = await this._datasetStorage.get(datasetId);\n\t\t\t\tif (!Is.empty(existingEntity) && existingEntity.ownerId !== trustInfo.identity) {\n\t\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetOwnerMismatch\", {\n\t\t\t\t\t\tdatasetId\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Bake the publishing organization into distribution accessService URLs\n\t\t\t\tawait this.bakeOrganizationIntoDistributions(datasetEntity, trustInfo.identity);\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"datasetSet\",\n\t\t\t\t\tdata: { datasetId }\n\t\t\t\t});\n\n\t\t\t\tconst filterNames = FederatedCatalogueFilterFactory.names();\n\t\t\t\tfor (const filterType of filterNames) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst filter = FederatedCatalogueFilterFactory.get(filterType);\n\t\t\t\t\t\tconst filterIndexes = await filter.createIndex(normalizedDataset);\n\n\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t\tmessage: \"filterIndexPersisted\",\n\t\t\t\t\t\t\tdata: { datasetId, filterType, indexCount: Object.keys(filterIndexes).length }\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t\tmessage: \"filterIndexCreationFailed\",\n\t\t\t\t\t\t\tdata: { datasetId, filterType },\n\t\t\t\t\t\t\terror: BaseError.fromError(error)\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tawait this._datasetStorage.set(datasetEntity);\n\n\t\t\t\treturn datasetId;\n\t\t\t} finally {\n\t\t\t\tMutex.unlock(datasetId);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn transformToCatalogError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Remove a dataset from the catalogue by its unique identifier.\n\t * Indexes are automatically removed as they are stored with the dataset.\n\t * @param datasetId The unique identifier of the dataset to remove.\n\t * @param trustPayload Optional payload for trust evaluation, if applicable.\n\t * @returns Nothing, or a CatalogError if an error occurs.\n\t */\n\tpublic async remove(\n\t\tdatasetId: string,\n\t\ttrustPayload: unknown\n\t): Promise<IDataspaceProtocolCatalogError | undefined> {\n\t\ttry {\n\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(datasetId), datasetId);\n\n\t\t\tconst trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, \"remove\");\n\n\t\t\tawait Mutex.lock(datasetId);\n\t\t\ttry {\n\t\t\t\tconst existingEntity = await this._datasetStorage.get(datasetId);\n\t\t\t\tif (!Is.empty(existingEntity) && existingEntity.ownerId !== trustInfo.identity) {\n\t\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetRemoveNotOwner\", {\n\t\t\t\t\t\tdatasetId\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"datasetRemove\",\n\t\t\t\t\tdata: { datasetId }\n\t\t\t\t});\n\n\t\t\t\tawait this._datasetStorage.remove(datasetId);\n\t\t\t} finally {\n\t\t\t\tMutex.unlock(datasetId);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn transformToCatalogError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Execute a query against the catalogue using registered filter plugins.\n\t * Returns a DS Protocol compliant Catalog object with participantId.\n\t *\n\t * The root catalog's participantId is the requesting participant (from context).\n\t * Own datasets (matching requestingParticipantId) go directly in root dataset[].\n\t * Other participants' datasets are grouped in nested catalog[] entries.\n\t *\n\t * For anonymous requests (no context), uses the first publisher found as fallback.\n\t * Returns CatalogError 404 when no datasets exist, CatalogError 400 for invalid requests.\n\t *\n\t * @param filter The filter criteria containing @type, optional cursor and limit properties.\n\t * @param cursor Optional cursor for pagination.\n\t * @param limit Optional limit for pagination.\n\t * @param trustPayload Optional payload for trust evaluation, if applicable.\n\t * @returns Complete IDataspaceProtocolCatalog with @context, @id, @type, participantId, dataset/catalog,\n\t * or CatalogError if validation fails or an error occurs.\n\t */\n\tpublic async query(\n\t\tfilter: unknown[] | undefined,\n\t\tcursor: string | undefined,\n\t\tlimit: number | undefined,\n\t\ttrustPayload: unknown\n\t): Promise<{\n\t\tresult: IDataspaceProtocolCatalog | IDataspaceProtocolCatalogError;\n\t\tcursor?: string;\n\t}> {\n\t\ttry {\n\t\t\tconst trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, \"query\");\n\n\t\t\tlet datasets: IDcatDataset[];\n\t\t\tlet resultCursor: string | undefined;\n\n\t\t\tif (!Is.empty(filter) && !Is.array(filter)) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"filterMustBeArray\");\n\t\t\t}\n\n\t\t\tif (!Is.arrayValue(filter)) {\n\t\t\t\tconst result = await this._datasetStorage.query(\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tcursor,\n\t\t\t\t\tlimit\n\t\t\t\t);\n\t\t\t\tdatasets = result.entities.map(entity => datasetEntityToModel(entity));\n\t\t\t\tresultCursor = result.cursor;\n\t\t\t} else if (filter.length > 1) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"multipleFiltersNotSupported\");\n\t\t\t} else {\n\t\t\t\tconst singleFilter = filter[0] as { \"@type\"?: string } | undefined;\n\n\t\t\t\tconst filterType = singleFilter?.[\"@type\"];\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"catalogQuery\",\n\t\t\t\t\tdata: { filterType: filterType ?? \"\", cursor: cursor ?? \"\", limit: limit ?? \"\" }\n\t\t\t\t});\n\n\t\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(filterType), filterType);\n\n\t\t\t\tconst selectedFilter = FederatedCatalogueFilterFactory.get(filterType);\n\n\t\t\t\tObjectHelper.propertyDelete(filter, \"@type\");\n\t\t\t\tconst result = await selectedFilter.query(trustInfo, filter, cursor, limit);\n\n\t\t\t\tdatasets = result.datasets;\n\t\t\t\tresultCursor = result.cursor;\n\t\t\t}\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"catalogQueryComplete\",\n\t\t\t\tdata: { resultCount: datasets.length, hasMore: Is.stringValue(resultCursor) }\n\t\t\t});\n\n\t\t\t// Return CatalogError 404 when no datasets exist\n\t\t\tif (!Is.arrayValue(datasets)) {\n\t\t\t\tthrow new NotFoundError(FederatedCatalogueService.CLASS_NAME, \"noDatasetsFound\");\n\t\t\t}\n\n\t\t\tlet requestingParticipantId = trustInfo.identity;\n\n\t\t\t// Group datasets by dcterms:publisher (participantId)\n\t\t\tconst datasetsByParticipant = new Map<string, IDcatDataset[]>();\n\t\t\tfor (const dataset of datasets) {\n\t\t\t\tconst publisher = this.extractPublisher(dataset);\n\t\t\t\tconst participantId = publisher ?? \"unknown\";\n\t\t\t\tconst existing = datasetsByParticipant.get(participantId) ?? [];\n\t\t\t\texisting.push(dataset);\n\t\t\t\tdatasetsByParticipant.set(participantId, existing);\n\t\t\t}\n\n\t\t\tconst participantIds = [...datasetsByParticipant.keys()];\n\n\t\t\t// For anonymous requests (no context), use first publisher as fallback\n\t\t\tif (!Is.stringValue(requestingParticipantId)) {\n\t\t\t\trequestingParticipantId = participantIds[0] ?? \"unknown\";\n\t\t\t}\n\n\t\t\t// Separate own datasets from other participants' datasets\n\t\t\tconst ownDatasets = datasetsByParticipant.get(requestingParticipantId) ?? [];\n\t\t\tconst otherParticipantIds = participantIds.filter(id => id !== requestingParticipantId);\n\n\t\t\tlet catalog: IDataspaceProtocolCatalog;\n\n\t\t\tif (!Is.arrayValue(otherParticipantIds)) {\n\t\t\t\t// Only own datasets (or all datasets belong to requesting participant)\n\t\t\t\tconst catalogId = this.generateCatalogId(ownDatasets, requestingParticipantId);\n\n\t\t\t\tcatalog = {\n\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\"@id\": catalogId,\n\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\t\t\tparticipantId: requestingParticipantId,\n\t\t\t\t\tdataset: ownDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"]\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t// Mixed: own datasets at root level, others in nested catalogs\n\t\t\t\tconst nestedCatalogs: IDataspaceProtocolCatalog[] = [];\n\n\t\t\t\tfor (const participantId of otherParticipantIds) {\n\t\t\t\t\tconst participantDatasets = datasetsByParticipant.get(participantId) ?? [];\n\t\t\t\t\tconst subCatalogId = this.generateCatalogId(participantDatasets, participantId);\n\n\t\t\t\t\tconst subCatalog: IDataspaceProtocolCatalog = {\n\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\"@id\": subCatalogId,\n\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\t\t\t\tparticipantId,\n\t\t\t\t\t\tdataset: participantDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"]\n\t\t\t\t\t};\n\n\t\t\t\t\tnestedCatalogs.push(subCatalog);\n\t\t\t\t}\n\n\t\t\t\t// Root catalog contains own datasets and nested catalogs for others\n\t\t\t\tconst rootCatalogId = this.generateCatalogId(datasets, requestingParticipantId);\n\n\t\t\t\tcatalog = {\n\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\"@id\": rootCatalogId,\n\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\t\t\tparticipantId: requestingParticipantId,\n\t\t\t\t\tdataset: ownDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"],\n\t\t\t\t\tcatalog: nestedCatalogs\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Normalize to DS Protocol compliant format\n\t\t\t// This ensures the payload matches exactly what the DS Protocol mandates\n\t\t\tconst normalizedCatalog = await DataspaceProtocolHelper.normalize(\n\t\t\t\tJsonLdHelper.toNodeObject(catalog)\n\t\t\t);\n\n\t\t\tconst structuredResult: IDataspaceProtocolCatalog =\n\t\t\t\tJsonLdHelper.toStructuredObject(normalizedCatalog);\n\n\t\t\treturn {\n\t\t\t\tresult: structuredResult,\n\t\t\t\tcursor: resultCursor\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tresult: transformToCatalogError(error)\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Extract publisher from dataset.\n\t * Publisher can be a string or an IFoafAgent object with @id.\n\t * @param dataset The dataset to extract publisher from.\n\t * @returns The publisher string or undefined if not found.\n\t * @internal\n\t */\n\tprivate extractPublisher(dataset: IDcatDataset): string | undefined {\n\t\tconst publisher = dataset[\"dcterms:publisher\"];\n\n\t\t// Handle case where publisher is an object with @id (IFoafAgent)\n\t\tif (Is.object<{ \"@id\"?: string }>(publisher)) {\n\t\t\tconst publisherId = publisher[\"@id\"];\n\t\t\treturn Is.stringValue(publisherId) ? publisherId : undefined;\n\t\t}\n\n\t\treturn Is.stringValue(publisher) ? publisher : undefined;\n\t}\n\n\t/**\n\t * Bake the publishing organization token into each distribution's accessService URL.\n\t * @param entity The dataset entity to modify in place.\n\t * @param organizationId The publishing organization id to bake.\n\t * @internal\n\t */\n\tprivate async bakeOrganizationIntoDistributions(\n\t\tentity: Dataset,\n\t\torganizationId: string\n\t): Promise<void> {\n\t\t// Storage uses the prefixed JSON-LD key \"dcat:accessService\" (not the unprefixed\n\t\t// \"accessService\" that appears in compacted query responses).\n\t\tconst distributions = ArrayHelper.fromObjectOrArray(entity[\"dcat:distribution\"]) ?? [];\n\t\tfor (const dist of distributions) {\n\t\t\tconst accessService = dist?.[\"dcat:accessService\"];\n\t\t\tif (Is.stringValue(accessService)) {\n\t\t\t\tdist[\"dcat:accessService\"] = HttpUrlHelper.addQueryStringParam(\n\t\t\t\t\taccessService,\n\t\t\t\t\tContextIdKeys.Organization,\n\t\t\t\t\torganizationId\n\t\t\t\t);\n\t\t\t} else if (Is.objectValue(accessService)) {\n\t\t\t\t// DCAT object form: a dcat:DataService with an endpointURL.\n\t\t\t\tconst dataService = accessService as { \"dcat:endpointURL\"?: unknown };\n\t\t\t\tif (Is.stringValue(dataService[\"dcat:endpointURL\"])) {\n\t\t\t\t\tdataService[\"dcat:endpointURL\"] = HttpUrlHelper.addQueryStringParam(\n\t\t\t\t\t\tdataService[\"dcat:endpointURL\"],\n\t\t\t\t\t\tContextIdKeys.Organization,\n\t\t\t\t\t\torganizationId\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Generate a deterministic catalog ID using canonical hash.\n\t * @param datasets The datasets to include in the hash.\n\t * @param participantId The participant ID to include in the hash.\n\t * @returns A URN-formatted catalog ID.\n\t * @internal\n\t */\n\tprivate generateCatalogId(datasets: IDcatDataset[], participantId: string): string {\n\t\tconst datasetIds = datasets\n\t\t\t.map(d => d[\"@id\"])\n\t\t\t.filter(id => Is.stringValue(id))\n\t\t\t.sort();\n\n\t\tconst canonicalContent = JsonHelper.canonicalize({\n\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\tparticipantId,\n\t\t\tdatasets: datasetIds\n\t\t});\n\n\t\tconst canonicalBytes = Converter.utf8ToBytes(canonicalContent);\n\t\tconst catalogHash = Converter.bytesToHex(Blake2b.sum256(canonicalBytes));\n\t\treturn `urn:x-catalog:${catalogHash}`;\n\t}\n}\n"]}
1
+ {"version":3,"file":"federatedCatalogueService.js","sourceRoot":"","sources":["../../../src/services/federatedCatalogueService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { HttpUrlHelper } from \"@twin.org/api-models\";\nimport { ContextIdKeys } from \"@twin.org/context\";\nimport {\n\tArrayHelper,\n\tBaseError,\n\tCoerce,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tJsonHelper,\n\tMutex,\n\tNotFoundError,\n\tObjectHelper,\n\tUrl,\n\tUrn,\n\tValidation\n} from \"@twin.org/core\";\nimport { Blake2b } from \"@twin.org/crypto\";\nimport { JsonLdHelper, JsonLdProcessor } from \"@twin.org/data-json-ld\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport {\n\tFederatedCatalogueFilterFactory,\n\tFederatedCatalogueMetricIds,\n\tFederatedCatalogueMetrics,\n\ttype IFederatedCatalogueComponent\n} from \"@twin.org/federated-catalogue-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tDataspaceProtocolCatalogTypes,\n\tDataspaceProtocolContexts,\n\tDataspaceProtocolDataTypes,\n\tDataspaceProtocolHelper,\n\ttype IDataspaceProtocolCatalog,\n\ttype IDataspaceProtocolCatalogError\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { DublinCoreContexts, DublinCoreDataTypes } from \"@twin.org/standards-dublin-core\";\nimport { FoafDataTypes } from \"@twin.org/standards-foaf\";\nimport {\n\tDcatContexts,\n\tDcatDataTypes,\n\ttype DcatContextType,\n\ttype IDcatDataset\n} from \"@twin.org/standards-w3c-dcat\";\nimport { OdrlContexts } from \"@twin.org/standards-w3c-odrl\";\nimport { MetricHelper, type ITelemetryComponent } from \"@twin.org/telemetry-models\";\nimport { TrustHelper, type ITrustComponent } from \"@twin.org/trust-models\";\nimport type { Dataset } from \"../entities/dataset.js\";\nimport type { IFederatedCatalogueServiceConstructorOptions } from \"../models/IFederatedCatalogueServiceConstructorOptions.js\";\nimport { transformToCatalogError } from \"../utils/catalogErrorUtils.js\";\nimport { datasetEntityToModel, datasetModelToEntity } from \"../utils/datasetConverters.js\";\n\n/**\n * Service for managing federated catalogue operations.\n * Provides Dataspace Protocol-compliant catalog endpoints for dataset registry and query.\n */\nexport class FederatedCatalogueService implements IFederatedCatalogueComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<FederatedCatalogueService>();\n\n\t/**\n\t * The logging component for the federated catalogue service.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The entity storage connector for datasets.\n\t * @internal\n\t */\n\tprivate readonly _datasetStorage: IEntityStorageConnector<Dataset>;\n\n\t/**\n\t * The trust component for token verification and generation.\n\t * @internal\n\t */\n\tprivate readonly _trustComponent: ITrustComponent;\n\n\t/**\n\t * The optional telemetry component for event metrics.\n\t * @internal\n\t */\n\tprivate readonly _telemetryComponent?: ITelemetryComponent;\n\n\t/**\n\t * Timeout in milliseconds for acquiring a mutex lock.\n\t * @internal\n\t */\n\tprivate readonly _mutexTimeoutMs?: number;\n\n\t/**\n\t * Create a new instance of FederatedCatalogueService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IFederatedCatalogueServiceConstructorOptions) {\n\t\tthis._logging = ComponentFactory.getIfExists<ILoggingComponent>(options?.loggingComponentType);\n\n\t\tthis._datasetStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.datasetEntityStorageType ?? \"dataset\"\n\t\t);\n\n\t\tthis._trustComponent = ComponentFactory.get<ITrustComponent>(\n\t\t\toptions?.trustComponentType ?? \"trust\"\n\t\t);\n\n\t\tthis._telemetryComponent = ComponentFactory.getIfExists<ITelemetryComponent>(\n\t\t\toptions?.telemetryComponentType\n\t\t);\n\t\tthis._mutexTimeoutMs = Coerce.integer(options?.config?.mutexTimeoutMs);\n\n\t\t// Register JSON-LD redirects for offline processing\n\t\tDcatDataTypes.registerRedirects();\n\t\tDublinCoreDataTypes.registerRedirects();\n\t\tFoafDataTypes.registerRedirects();\n\t\tDataspaceProtocolDataTypes.registerRedirects();\n\n\t\t// Register DS Protocol data types for conformance checking\n\t\tDataspaceProtocolDataTypes.registerTypes();\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn FederatedCatalogueService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Register all federated catalogue metrics with the telemetry component.\n\t * @returns A promise that resolves when all metrics have been registered.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tawait MetricHelper.createMetrics(this._telemetryComponent, FederatedCatalogueMetrics);\n\t}\n\n\t/**\n\t * Retrieve a dataset by its unique identifier.\n\t * @param datasetId The unique identifier of the dataset.\n\t * @param trustPayload Optional payload for trust evaluation, if applicable.\n\t * @returns A promise that resolves with the dataset if found, or a CatalogError if not found or an error occurs.\n\t */\n\tpublic async get(\n\t\tdatasetId: string,\n\t\ttrustPayload: unknown\n\t): Promise<IDcatDataset | IDataspaceProtocolCatalogError> {\n\t\ttry {\n\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(datasetId), datasetId);\n\t\t\tawait TrustHelper.verifyTrust(this._trustComponent, trustPayload, \"get\");\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"datasetRetrieve\",\n\t\t\t\tdata: { datasetId }\n\t\t\t});\n\n\t\t\tconst datasetEntity = await this._datasetStorage.get(datasetId);\n\n\t\t\tif (!datasetEntity) {\n\t\t\t\tthrow new NotFoundError(FederatedCatalogueService.CLASS_NAME, \"datasetNotFound\", datasetId);\n\t\t\t}\n\n\t\t\tconst dataset = datasetEntityToModel(datasetEntity);\n\n\t\t\t// Normalize to DS Protocol compliant format\n\t\t\t// This ensures the payload matches exactly what the DS Protocol mandates\n\t\t\tconst normalizedDataset = await DataspaceProtocolHelper.normalize(\n\t\t\t\tJsonLdHelper.toNodeObject(dataset)\n\t\t\t);\n\n\t\t\tconst structured = JsonLdHelper.toStructuredObject<IDcatDataset>(normalizedDataset);\n\n\t\t\tawait MetricHelper.metricIncrement(\n\t\t\t\tthis._telemetryComponent,\n\t\t\t\tFederatedCatalogueMetricIds.DatasetsRetrieved\n\t\t\t);\n\n\t\t\treturn structured;\n\t\t} catch (error) {\n\t\t\treturn transformToCatalogError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Insert or update a dataset in the catalogue.\n\t * @param dataset The dataset to store.\n\t * @param trustPayload Optional payload for trust evaluation, if applicable.\n\t * @returns A promise that resolves with the unique identifier of the stored dataset, or a CatalogError if an error occurs.\n\t */\n\tpublic async set(\n\t\tdataset: IDcatDataset,\n\t\ttrustPayload: unknown\n\t): Promise<string | IDataspaceProtocolCatalogError> {\n\t\ttry {\n\t\t\tGuards.object(FederatedCatalogueService.CLASS_NAME, nameof(dataset), dataset);\n\n\t\t\tconst trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, \"set\");\n\n\t\t\t// Normalize @id from dcterms:identifier if provided\n\t\t\tconst datasetId = dataset[\"@id\"] ?? dataset[\"dcterms:identifier\"];\n\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(datasetId), datasetId);\n\n\t\t\t// Set @id if it was derived from dcterms:identifier\n\t\t\tif (Is.empty(dataset[\"@id\"]) && !Is.empty(dataset[\"dcterms:identifier\"])) {\n\t\t\t\tdataset[\"@id\"] = datasetId;\n\t\t\t}\n\n\t\t\t// Validate @id is a valid URI (URN or URL) per DS Protocol\n\t\t\tconst isValidUrn = !Is.empty(Urn.tryParseExact(datasetId));\n\t\t\tconst isValidUrl = !Is.empty(Url.tryParseExact(datasetId));\n\t\t\tif (!isValidUrn && !isValidUrl) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetIdInvalidUri\", {\n\t\t\t\t\tdatasetId\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Validate @type exists\n\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, \"@type\", dataset[\"@type\"]);\n\n\t\t\t// Validate dcterms:publisher exists (required for multi-participant catalog)\n\t\t\t// The publisher is used to derive participantId when returning catalog query results\n\t\t\tconst publisher = dataset[\"dcterms:publisher\"];\n\t\t\tif (Is.empty(publisher)) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetMissingPublisher\", {\n\t\t\t\t\tdatasetId\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// DS Protocol compliance validation\n\t\t\tconst validationFailures = await DataspaceProtocolHelper.validate(\n\t\t\t\tJsonLdHelper.toNodeObject(dataset)\n\t\t\t);\n\n\t\t\tValidation.asValidationError(\n\t\t\t\tFederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\"dataset\",\n\t\t\t\tvalidationFailures\n\t\t\t);\n\n\t\t\t// Normalize dataset for storage using JSON-LD compaction\n\t\t\t// This ensures the dataset uses prefixed properties that entity storage expects\n\t\t\t// Entity storage schema uses DCAT-prefixed properties (dcat:distribution, not distribution)\n\t\t\t// Use a standard context with prefixes to ensure proper normalization\n\t\t\tconst storageContext: DcatContextType = {\n\t\t\t\tdcat: DcatContexts.Namespace,\n\t\t\t\tdcterms: DublinCoreContexts.NamespaceTerms,\n\t\t\t\todrl: OdrlContexts.Namespace\n\t\t\t};\n\t\t\tconst normalizedDataset = await JsonLdProcessor.compact(dataset, storageContext);\n\n\t\t\tconst datasetEntity = datasetModelToEntity(normalizedDataset, trustInfo.identity);\n\n\t\t\t// Serialise the read-check-write cycle so concurrent requests for the\n\t\t\t// same dataset cannot both pass the ownership check and overwrite each other.\n\t\t\tawait Mutex.lock(datasetId, { throwOnTimeout: true, timeoutMs: this._mutexTimeoutMs });\n\t\t\ttry {\n\t\t\t\tconst existingEntity = await this._datasetStorage.get(datasetId);\n\t\t\t\tif (!Is.empty(existingEntity) && existingEntity.ownerId !== trustInfo.identity) {\n\t\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetOwnerMismatch\", {\n\t\t\t\t\t\tdatasetId\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Bake the publishing organization into distribution accessService URLs\n\t\t\t\tawait this.bakeOrganizationIntoDistributions(datasetEntity, trustInfo.identity);\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"datasetSet\",\n\t\t\t\t\tdata: { datasetId }\n\t\t\t\t});\n\n\t\t\t\tconst filterNames = FederatedCatalogueFilterFactory.names();\n\t\t\t\tfor (const filterType of filterNames) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst filter = FederatedCatalogueFilterFactory.get(filterType);\n\t\t\t\t\t\tconst filterIndexes = await filter.createIndex(normalizedDataset);\n\n\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t\tmessage: \"filterIndexPersisted\",\n\t\t\t\t\t\t\tdata: { datasetId, filterType, indexCount: Object.keys(filterIndexes).length }\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tawait MetricHelper.metricIncrement(\n\t\t\t\t\t\t\tthis._telemetryComponent,\n\t\t\t\t\t\t\tFederatedCatalogueMetricIds.FilterIndexesCreated,\n\t\t\t\t\t\t\t{ filterType, indexCount: Object.keys(filterIndexes).length }\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t\tmessage: \"filterIndexCreationFailed\",\n\t\t\t\t\t\t\tdata: { datasetId, filterType },\n\t\t\t\t\t\t\terror: BaseError.fromError(error)\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tawait MetricHelper.metricIncrement(\n\t\t\t\t\t\t\tthis._telemetryComponent,\n\t\t\t\t\t\t\tFederatedCatalogueMetricIds.FilterIndexFailures,\n\t\t\t\t\t\t\t{ filterType }\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tawait this._datasetStorage.set(datasetEntity);\n\n\t\t\t\tawait MetricHelper.metricIncrement(\n\t\t\t\t\tthis._telemetryComponent,\n\t\t\t\t\tFederatedCatalogueMetricIds.DatasetsStored\n\t\t\t\t);\n\n\t\t\t\treturn datasetId;\n\t\t\t} finally {\n\t\t\t\tMutex.unlock(datasetId);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn transformToCatalogError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Remove a dataset from the catalogue by its unique identifier.\n\t * Indexes are automatically removed as they are stored with the dataset.\n\t * @param datasetId The unique identifier of the dataset to remove.\n\t * @param trustPayload Optional payload for trust evaluation, if applicable.\n\t * @returns A promise that resolves with undefined on success, or a CatalogError if removal fails.\n\t */\n\tpublic async remove(\n\t\tdatasetId: string,\n\t\ttrustPayload: unknown\n\t): Promise<IDataspaceProtocolCatalogError | undefined> {\n\t\ttry {\n\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(datasetId), datasetId);\n\n\t\t\tconst trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, \"remove\");\n\n\t\t\tawait Mutex.lock(datasetId, { throwOnTimeout: true, timeoutMs: this._mutexTimeoutMs });\n\t\t\ttry {\n\t\t\t\tconst existingEntity = await this._datasetStorage.get(datasetId);\n\t\t\t\tif (!Is.empty(existingEntity) && existingEntity.ownerId !== trustInfo.identity) {\n\t\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetRemoveNotOwner\", {\n\t\t\t\t\t\tdatasetId\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"datasetRemove\",\n\t\t\t\t\tdata: { datasetId }\n\t\t\t\t});\n\n\t\t\t\tawait this._datasetStorage.remove(datasetId);\n\n\t\t\t\tawait MetricHelper.metricIncrement(\n\t\t\t\t\tthis._telemetryComponent,\n\t\t\t\t\tFederatedCatalogueMetricIds.DatasetsRemoved\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tMutex.unlock(datasetId);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn transformToCatalogError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Execute a query against the catalogue using registered filter plugins.\n\t * Returns a Dataspace Protocol compliant Catalog object with participantId.\n\t *\n\t * The root catalog's participantId is the requesting participant (from context).\n\t * Own datasets (matching requestingParticipantId) go directly in root dataset[].\n\t * Other participants' datasets are grouped in nested catalog[] entries.\n\t *\n\t * For anonymous requests (no context), uses the first publisher found as fallback.\n\t * Returns a CatalogError with status 404 when no datasets exist, or status 400 for invalid requests.\n\t *\n\t * @param filter The filter criteria array, where the first element contains @type.\n\t * @param cursor Optional cursor for pagination.\n\t * @param limit Optional limit for pagination.\n\t * @param trustPayload Optional payload for trust evaluation, if applicable.\n\t * @returns A promise that resolves with the catalog result and optional next-page cursor.\n\t */\n\tpublic async query(\n\t\tfilter: unknown[] | undefined,\n\t\tcursor: string | undefined,\n\t\tlimit: number | undefined,\n\t\ttrustPayload: unknown\n\t): Promise<{\n\t\tresult: IDataspaceProtocolCatalog | IDataspaceProtocolCatalogError;\n\t\tcursor?: string;\n\t}> {\n\t\ttry {\n\t\t\tconst trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, \"query\");\n\n\t\t\tlet datasets: IDcatDataset[];\n\t\t\tlet resultCursor: string | undefined;\n\n\t\t\tif (!Is.empty(filter) && !Is.array(filter)) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"filterMustBeArray\");\n\t\t\t}\n\n\t\t\tif (!Is.arrayValue(filter)) {\n\t\t\t\tconst result = await this._datasetStorage.query(\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tcursor,\n\t\t\t\t\tlimit\n\t\t\t\t);\n\t\t\t\tdatasets = result.entities.map(entity => datasetEntityToModel(entity));\n\t\t\t\tresultCursor = result.cursor;\n\t\t\t} else if (filter.length > 1) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"multipleFiltersNotSupported\");\n\t\t\t} else {\n\t\t\t\tconst singleFilter = filter[0] as { \"@type\"?: string } | undefined;\n\n\t\t\t\tconst filterType = singleFilter?.[\"@type\"];\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"catalogQuery\",\n\t\t\t\t\tdata: { filterType: filterType ?? \"\", cursor: cursor ?? \"\", limit: limit ?? \"\" }\n\t\t\t\t});\n\n\t\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(filterType), filterType);\n\n\t\t\t\tconst selectedFilter = FederatedCatalogueFilterFactory.get(filterType);\n\n\t\t\t\tObjectHelper.propertyDelete(filter, \"@type\");\n\t\t\t\tconst result = await selectedFilter.query(trustInfo, filter, cursor, limit);\n\n\t\t\t\tdatasets = result.datasets;\n\t\t\t\tresultCursor = result.cursor;\n\t\t\t}\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"catalogQueryComplete\",\n\t\t\t\tdata: { resultCount: datasets.length, hasMore: Is.stringValue(resultCursor) }\n\t\t\t});\n\n\t\t\tawait MetricHelper.metricIncrement(\n\t\t\t\tthis._telemetryComponent,\n\t\t\t\tFederatedCatalogueMetricIds.QueriesExecuted,\n\t\t\t\t{ resultCount: datasets.length, hasMore: Is.stringValue(resultCursor) }\n\t\t\t);\n\n\t\t\t// Return CatalogError 404 when no datasets exist\n\t\t\tif (!Is.arrayValue(datasets)) {\n\t\t\t\tthrow new NotFoundError(FederatedCatalogueService.CLASS_NAME, \"noDatasetsFound\");\n\t\t\t}\n\n\t\t\tlet requestingParticipantId = trustInfo.identity;\n\n\t\t\t// Group datasets by dcterms:publisher (participantId)\n\t\t\tconst datasetsByParticipant = new Map<string, IDcatDataset[]>();\n\t\t\tfor (const dataset of datasets) {\n\t\t\t\tconst publisher = this.extractPublisher(dataset);\n\t\t\t\tconst participantId = publisher ?? \"unknown\";\n\t\t\t\tconst existing = datasetsByParticipant.get(participantId) ?? [];\n\t\t\t\texisting.push(dataset);\n\t\t\t\tdatasetsByParticipant.set(participantId, existing);\n\t\t\t}\n\n\t\t\tconst participantIds = [...datasetsByParticipant.keys()];\n\n\t\t\t// For anonymous requests (no context), use first publisher as fallback\n\t\t\tif (!Is.stringValue(requestingParticipantId)) {\n\t\t\t\trequestingParticipantId = participantIds[0] ?? \"unknown\";\n\t\t\t}\n\n\t\t\t// Separate own datasets from other participants' datasets\n\t\t\tconst ownDatasets = datasetsByParticipant.get(requestingParticipantId) ?? [];\n\t\t\tconst otherParticipantIds = participantIds.filter(id => id !== requestingParticipantId);\n\n\t\t\tlet catalog: IDataspaceProtocolCatalog;\n\n\t\t\tif (!Is.arrayValue(otherParticipantIds)) {\n\t\t\t\t// Only own datasets (or all datasets belong to requesting participant)\n\t\t\t\tconst catalogId = this.generateCatalogId(ownDatasets, requestingParticipantId);\n\n\t\t\t\tcatalog = {\n\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\"@id\": catalogId,\n\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\t\t\tparticipantId: requestingParticipantId,\n\t\t\t\t\tdataset: ownDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"]\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t// Mixed: own datasets at root level, others in nested catalogs\n\t\t\t\tconst nestedCatalogs: IDataspaceProtocolCatalog[] = [];\n\n\t\t\t\tfor (const participantId of otherParticipantIds) {\n\t\t\t\t\tconst participantDatasets = datasetsByParticipant.get(participantId) ?? [];\n\t\t\t\t\tconst subCatalogId = this.generateCatalogId(participantDatasets, participantId);\n\n\t\t\t\t\tconst subCatalog: IDataspaceProtocolCatalog = {\n\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\"@id\": subCatalogId,\n\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\t\t\t\tparticipantId,\n\t\t\t\t\t\tdataset: participantDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"]\n\t\t\t\t\t};\n\n\t\t\t\t\tnestedCatalogs.push(subCatalog);\n\t\t\t\t}\n\n\t\t\t\t// Root catalog contains own datasets and nested catalogs for others\n\t\t\t\tconst rootCatalogId = this.generateCatalogId(datasets, requestingParticipantId);\n\n\t\t\t\tcatalog = {\n\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\"@id\": rootCatalogId,\n\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\t\t\tparticipantId: requestingParticipantId,\n\t\t\t\t\tdataset: ownDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"],\n\t\t\t\t\tcatalog: nestedCatalogs\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Normalize to DS Protocol compliant format\n\t\t\t// This ensures the payload matches exactly what the DS Protocol mandates\n\t\t\tconst normalizedCatalog = await DataspaceProtocolHelper.normalize(\n\t\t\t\tJsonLdHelper.toNodeObject(catalog)\n\t\t\t);\n\n\t\t\tconst structuredResult: IDataspaceProtocolCatalog =\n\t\t\t\tJsonLdHelper.toStructuredObject(normalizedCatalog);\n\n\t\t\treturn {\n\t\t\t\tresult: structuredResult,\n\t\t\t\tcursor: resultCursor\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tresult: transformToCatalogError(error)\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Extract publisher from dataset.\n\t * Publisher can be a string or an IFoafAgent object with @id.\n\t * @param dataset The dataset to extract publisher from.\n\t * @returns The publisher string or undefined if not found.\n\t * @internal\n\t */\n\tprivate extractPublisher(dataset: IDcatDataset): string | undefined {\n\t\tconst publisher = dataset[\"dcterms:publisher\"];\n\n\t\t// Handle case where publisher is an object with @id (IFoafAgent)\n\t\tif (Is.object<{ \"@id\"?: string }>(publisher)) {\n\t\t\tconst publisherId = publisher[\"@id\"];\n\t\t\treturn Is.stringValue(publisherId) ? publisherId : undefined;\n\t\t}\n\n\t\treturn Is.stringValue(publisher) ? publisher : undefined;\n\t}\n\n\t/**\n\t * Bake the publishing organization token into each distribution's accessService URL.\n\t * @param entity The dataset entity to modify in place.\n\t * @param organizationId The publishing organization id to bake.\n\t * @returns A promise that resolves when all distribution URLs have been updated.\n\t * @internal\n\t */\n\tprivate async bakeOrganizationIntoDistributions(\n\t\tentity: Dataset,\n\t\torganizationId: string\n\t): Promise<void> {\n\t\t// Storage uses the prefixed JSON-LD key \"dcat:accessService\" (not the unprefixed\n\t\t// \"accessService\" that appears in compacted query responses).\n\t\tconst distributions = ArrayHelper.fromObjectOrArray(entity[\"dcat:distribution\"]) ?? [];\n\t\tfor (const dist of distributions) {\n\t\t\tconst accessService = dist?.[\"dcat:accessService\"];\n\t\t\tif (Is.stringValue(accessService)) {\n\t\t\t\tdist[\"dcat:accessService\"] = HttpUrlHelper.addQueryStringParam(\n\t\t\t\t\taccessService,\n\t\t\t\t\tContextIdKeys.Organization,\n\t\t\t\t\torganizationId\n\t\t\t\t);\n\t\t\t} else if (Is.objectValue(accessService)) {\n\t\t\t\t// DCAT object form: a dcat:DataService with an endpointURL.\n\t\t\t\tconst dataService = accessService as { \"dcat:endpointURL\"?: unknown };\n\t\t\t\tif (Is.stringValue(dataService[\"dcat:endpointURL\"])) {\n\t\t\t\t\tdataService[\"dcat:endpointURL\"] = HttpUrlHelper.addQueryStringParam(\n\t\t\t\t\t\tdataService[\"dcat:endpointURL\"],\n\t\t\t\t\t\tContextIdKeys.Organization,\n\t\t\t\t\t\torganizationId\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Generate a deterministic catalog ID using canonical hash.\n\t * @param datasets The datasets to include in the hash.\n\t * @param participantId The participant ID to include in the hash.\n\t * @returns A URN-formatted catalog ID.\n\t * @internal\n\t */\n\tprivate generateCatalogId(datasets: IDcatDataset[], participantId: string): string {\n\t\tconst datasetIds = datasets\n\t\t\t.map(d => d[\"@id\"])\n\t\t\t.filter(id => Is.stringValue(id))\n\t\t\t.sort();\n\n\t\tconst canonicalContent = JsonHelper.canonicalize({\n\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\tparticipantId,\n\t\t\tdatasets: datasetIds\n\t\t});\n\n\t\tconst canonicalBytes = Converter.utf8ToBytes(canonicalContent);\n\t\tconst catalogHash = Converter.bytesToHex(Blake2b.sum256(canonicalBytes));\n\t\treturn `urn:x-catalog:${catalogHash}`;\n\t}\n}\n"]}
@@ -5,10 +5,9 @@ import { BaseError, Is } from "@twin.org/core";
5
5
  import { DataspaceProtocolCatalogTypes, DataspaceProtocolContexts } from "@twin.org/standards-dataspace-protocol";
6
6
  import { HttpStatusCode } from "@twin.org/web";
7
7
  /**
8
- * Transform an error to DS Protocol CatalogError format.
9
- * Used by both service and route layers to ensure consistent error responses.
8
+ * Transform an error to Dataspace Protocol CatalogError format.
10
9
  * @param error The error to transform.
11
- * @returns The CatalogError.
10
+ * @returns A CatalogError object with the error code and flattened reason array.
12
11
  */
13
12
  export function transformToCatalogError(error) {
14
13
  const flattened = BaseError.flatten(error);
@@ -26,9 +25,9 @@ export function transformToCatalogError(error) {
26
25
  };
27
26
  }
28
27
  /**
29
- * Transform the DS Protocol result to an HTTP status code.
30
- * @param result The result to transform.
31
- * @returns The transformed status code or undefined if no transformation was found or not an error.
28
+ * Map a Dataspace Protocol result to an HTTP status code.
29
+ * @param result The result to evaluate.
30
+ * @returns The HTTP status code if the result is a CatalogError or an error object, otherwise undefined.
32
31
  */
33
32
  export function transformErrorToStatusCode(result) {
34
33
  // Is this an catalog error?
@@ -1 +1 @@
1
- {"version":3,"file":"catalogErrorUtils.js","sourceRoot":"","sources":["../../../src/utils/catalogErrorUtils.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAe,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EACN,6BAA6B,EAC7B,yBAAyB,EAEzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACrD,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAE3C,2EAA2E;IAC3E,iEAAiE;IACjE,wFAAwF;IACxF,wEAAwE;IACxE,+DAA+D;IAC/D,gJAAgJ;IAChJ,OAAO;QACN,UAAU,EAAE,yBAAyB,CAAC,OAAO;QAC7C,OAAO,EAAE,6BAA6B,CAAC,YAAY;QACnD,IAAI,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE;QACpD,MAAM,EAAE,SAAS;KACjB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,MAAe;IACzD,4BAA4B;IAC5B,IACC,EAAE,CAAC,MAAM,CAAiC,MAAM,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,KAAK,6BAA6B,CAAC,YAAY,EAC7D,CAAC;QACF,sEAAsE;QACtE,wCAAwC;QACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,OAAO,eAAe,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,cAAc,CAAC,UAAU,CAAC;IAC9E,CAAC;IAED,qBAAqB;IACrB,IAAI,EAAE,CAAC,MAAM,CAAS,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7D,kFAAkF;QAClF,OAAO,eAAe,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,UAAU,CAAC;IACjF,CAAC;IAED,OAAO,SAAS,CAAC;AAClB,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { HttpErrorHelper } from \"@twin.org/api-models\";\nimport { BaseError, type IError, Is } from \"@twin.org/core\";\nimport {\n\tDataspaceProtocolCatalogTypes,\n\tDataspaceProtocolContexts,\n\ttype IDataspaceProtocolCatalogError\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { HttpStatusCode } from \"@twin.org/web\";\n\n/**\n * Transform an error to DS Protocol CatalogError format.\n * Used by both service and route layers to ensure consistent error responses.\n * @param error The error to transform.\n * @returns The CatalogError.\n */\nexport function transformToCatalogError(error: unknown): IDataspaceProtocolCatalogError {\n\tconst flattened = BaseError.flatten(error);\n\n\t// We maintain the reason as the flattened array of errors for more context\n\t// this also helps preserve the original error messages and types\n\t// The code property can be any machine-readable string, we use the top-level error name\n\t// The schema allows for an array of any objects for the reason property\n\t// and is not limited to just strings or specific error formats\n\t// https://github.com/eclipse-dataspace-protocol-base/DataspaceProtocol/blob/main/artifacts/src/main/resources/catalog/catalog-error-schema.json\n\treturn {\n\t\t\"@context\": DataspaceProtocolContexts.Context,\n\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogError,\n\t\tcode: `${flattened[0].name}:${flattened[0].message}`,\n\t\treason: flattened\n\t};\n}\n\n/**\n * Transform the DS Protocol result to an HTTP status code.\n * @param result The result to transform.\n * @returns The transformed status code or undefined if no transformation was found or not an error.\n */\nexport function transformErrorToStatusCode(result: unknown): HttpStatusCode | undefined {\n\t// Is this an catalog error?\n\tif (\n\t\tIs.object<IDataspaceProtocolCatalogError>(result) &&\n\t\tresult[\"@type\"] === DataspaceProtocolCatalogTypes.CatalogError\n\t) {\n\t\t// As per the method above the result.code is the IError name property\n\t\t// so we use that to map to status codes\n\t\tconst codePart = result.code.split(\":\")[0];\n\t\treturn HttpErrorHelper.ERROR_TYPE_MAP[codePart] ?? HttpStatusCode.badRequest;\n\t}\n\n\t// Or a regular error\n\tif (Is.object<IError>(result) && !BaseError.isEmpty(result)) {\n\t\t// If this is a regular error, we can use the name property to map to status codes\n\t\treturn HttpErrorHelper.ERROR_TYPE_MAP[result.name] ?? HttpStatusCode.badRequest;\n\t}\n\n\treturn undefined;\n}\n"]}
1
+ {"version":3,"file":"catalogErrorUtils.js","sourceRoot":"","sources":["../../../src/utils/catalogErrorUtils.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAe,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EACN,6BAA6B,EAC7B,yBAAyB,EAEzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACrD,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAE3C,2EAA2E;IAC3E,iEAAiE;IACjE,wFAAwF;IACxF,wEAAwE;IACxE,+DAA+D;IAC/D,gJAAgJ;IAChJ,OAAO;QACN,UAAU,EAAE,yBAAyB,CAAC,OAAO;QAC7C,OAAO,EAAE,6BAA6B,CAAC,YAAY;QACnD,IAAI,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE;QACpD,MAAM,EAAE,SAAS;KACjB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,MAAe;IACzD,4BAA4B;IAC5B,IACC,EAAE,CAAC,MAAM,CAAiC,MAAM,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,KAAK,6BAA6B,CAAC,YAAY,EAC7D,CAAC;QACF,sEAAsE;QACtE,wCAAwC;QACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,OAAO,eAAe,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,cAAc,CAAC,UAAU,CAAC;IAC9E,CAAC;IAED,qBAAqB;IACrB,IAAI,EAAE,CAAC,MAAM,CAAS,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7D,kFAAkF;QAClF,OAAO,eAAe,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,UAAU,CAAC;IACjF,CAAC;IAED,OAAO,SAAS,CAAC;AAClB,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { HttpErrorHelper } from \"@twin.org/api-models\";\nimport { BaseError, type IError, Is } from \"@twin.org/core\";\nimport {\n\tDataspaceProtocolCatalogTypes,\n\tDataspaceProtocolContexts,\n\ttype IDataspaceProtocolCatalogError\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { HttpStatusCode } from \"@twin.org/web\";\n\n/**\n * Transform an error to Dataspace Protocol CatalogError format.\n * @param error The error to transform.\n * @returns A CatalogError object with the error code and flattened reason array.\n */\nexport function transformToCatalogError(error: unknown): IDataspaceProtocolCatalogError {\n\tconst flattened = BaseError.flatten(error);\n\n\t// We maintain the reason as the flattened array of errors for more context\n\t// this also helps preserve the original error messages and types\n\t// The code property can be any machine-readable string, we use the top-level error name\n\t// The schema allows for an array of any objects for the reason property\n\t// and is not limited to just strings or specific error formats\n\t// https://github.com/eclipse-dataspace-protocol-base/DataspaceProtocol/blob/main/artifacts/src/main/resources/catalog/catalog-error-schema.json\n\treturn {\n\t\t\"@context\": DataspaceProtocolContexts.Context,\n\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogError,\n\t\tcode: `${flattened[0].name}:${flattened[0].message}`,\n\t\treason: flattened\n\t};\n}\n\n/**\n * Map a Dataspace Protocol result to an HTTP status code.\n * @param result The result to evaluate.\n * @returns The HTTP status code if the result is a CatalogError or an error object, otherwise undefined.\n */\nexport function transformErrorToStatusCode(result: unknown): HttpStatusCode | undefined {\n\t// Is this an catalog error?\n\tif (\n\t\tIs.object<IDataspaceProtocolCatalogError>(result) &&\n\t\tresult[\"@type\"] === DataspaceProtocolCatalogTypes.CatalogError\n\t) {\n\t\t// As per the method above the result.code is the IError name property\n\t\t// so we use that to map to status codes\n\t\tconst codePart = result.code.split(\":\")[0];\n\t\treturn HttpErrorHelper.ERROR_TYPE_MAP[codePart] ?? HttpStatusCode.badRequest;\n\t}\n\n\t// Or a regular error\n\tif (Is.object<IError>(result) && !BaseError.isEmpty(result)) {\n\t\t// If this is a regular error, we can use the name property to map to status codes\n\t\treturn HttpErrorHelper.ERROR_TYPE_MAP[result.name] ?? HttpStatusCode.badRequest;\n\t}\n\n\treturn undefined;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { IRestRoute, ITag } from "@twin.org/api-models";
1
+ import { type IRestRoute, type ITag } from "@twin.org/api-models";
2
2
  /**
3
3
  * The tag to associate with the routes.
4
4
  */
@@ -2,4 +2,8 @@
2
2
  * Configuration for the FederatedCatalogueService.
3
3
  */
4
4
  export interface IFederatedCatalogueServiceConfig {
5
+ /**
6
+ * Timeout in milliseconds for acquiring a mutex lock, defaults to 5000ms.
7
+ */
8
+ mutexTimeoutMs?: number;
5
9
  }
@@ -17,6 +17,10 @@ export interface IFederatedCatalogueServiceConstructorOptions {
17
17
  * @default trust
18
18
  */
19
19
  trustComponentType?: string;
20
+ /**
21
+ * The component type for the optional telemetry component used for event metrics, defaults to no telemetry.
22
+ */
23
+ telemetryComponentType?: string;
20
24
  /**
21
25
  * Configuration for the federated catalogue service.
22
26
  */
@@ -1,2 +1,5 @@
1
1
  import type { IRestRouteEntryPoint } from "@twin.org/api-models";
2
+ /**
3
+ * REST route entry points for the federated catalogue service.
4
+ */
2
5
  export declare const restEntryPoints: IRestRouteEntryPoint[];
@@ -21,18 +21,23 @@ export declare class FederatedCatalogueService implements IFederatedCatalogueCom
21
21
  * @returns The class name of the component.
22
22
  */
23
23
  className(): string;
24
+ /**
25
+ * Register all federated catalogue metrics with the telemetry component.
26
+ * @returns A promise that resolves when all metrics have been registered.
27
+ */
28
+ start(): Promise<void>;
24
29
  /**
25
30
  * Retrieve a dataset by its unique identifier.
26
31
  * @param datasetId The unique identifier of the dataset.
27
32
  * @param trustPayload Optional payload for trust evaluation, if applicable.
28
- * @returns The dataset if found, or a CatalogError if not found or an error occurs.
33
+ * @returns A promise that resolves with the dataset if found, or a CatalogError if not found or an error occurs.
29
34
  */
30
35
  get(datasetId: string, trustPayload: unknown): Promise<IDcatDataset | IDataspaceProtocolCatalogError>;
31
36
  /**
32
37
  * Insert or update a dataset in the catalogue.
33
38
  * @param dataset The dataset to store.
34
39
  * @param trustPayload Optional payload for trust evaluation, if applicable.
35
- * @returns The unique identifier of the stored dataset, or a CatalogError if an error occurs.
40
+ * @returns A promise that resolves with the unique identifier of the stored dataset, or a CatalogError if an error occurs.
36
41
  */
37
42
  set(dataset: IDcatDataset, trustPayload: unknown): Promise<string | IDataspaceProtocolCatalogError>;
38
43
  /**
@@ -40,26 +45,25 @@ export declare class FederatedCatalogueService implements IFederatedCatalogueCom
40
45
  * Indexes are automatically removed as they are stored with the dataset.
41
46
  * @param datasetId The unique identifier of the dataset to remove.
42
47
  * @param trustPayload Optional payload for trust evaluation, if applicable.
43
- * @returns Nothing, or a CatalogError if an error occurs.
48
+ * @returns A promise that resolves with undefined on success, or a CatalogError if removal fails.
44
49
  */
45
50
  remove(datasetId: string, trustPayload: unknown): Promise<IDataspaceProtocolCatalogError | undefined>;
46
51
  /**
47
52
  * Execute a query against the catalogue using registered filter plugins.
48
- * Returns a DS Protocol compliant Catalog object with participantId.
53
+ * Returns a Dataspace Protocol compliant Catalog object with participantId.
49
54
  *
50
55
  * The root catalog's participantId is the requesting participant (from context).
51
56
  * Own datasets (matching requestingParticipantId) go directly in root dataset[].
52
57
  * Other participants' datasets are grouped in nested catalog[] entries.
53
58
  *
54
59
  * For anonymous requests (no context), uses the first publisher found as fallback.
55
- * Returns CatalogError 404 when no datasets exist, CatalogError 400 for invalid requests.
60
+ * Returns a CatalogError with status 404 when no datasets exist, or status 400 for invalid requests.
56
61
  *
57
- * @param filter The filter criteria containing @type, optional cursor and limit properties.
62
+ * @param filter The filter criteria array, where the first element contains @type.
58
63
  * @param cursor Optional cursor for pagination.
59
64
  * @param limit Optional limit for pagination.
60
65
  * @param trustPayload Optional payload for trust evaluation, if applicable.
61
- * @returns Complete IDataspaceProtocolCatalog with @context, @id, @type, participantId, dataset/catalog,
62
- * or CatalogError if validation fails or an error occurs.
66
+ * @returns A promise that resolves with the catalog result and optional next-page cursor.
63
67
  */
64
68
  query(filter: unknown[] | undefined, cursor: string | undefined, limit: number | undefined, trustPayload: unknown): Promise<{
65
69
  result: IDataspaceProtocolCatalog | IDataspaceProtocolCatalogError;
@@ -1,15 +1,14 @@
1
1
  import { type IDataspaceProtocolCatalogError } from "@twin.org/standards-dataspace-protocol";
2
2
  import { HttpStatusCode } from "@twin.org/web";
3
3
  /**
4
- * Transform an error to DS Protocol CatalogError format.
5
- * Used by both service and route layers to ensure consistent error responses.
4
+ * Transform an error to Dataspace Protocol CatalogError format.
6
5
  * @param error The error to transform.
7
- * @returns The CatalogError.
6
+ * @returns A CatalogError object with the error code and flattened reason array.
8
7
  */
9
8
  export declare function transformToCatalogError(error: unknown): IDataspaceProtocolCatalogError;
10
9
  /**
11
- * Transform the DS Protocol result to an HTTP status code.
12
- * @param result The result to transform.
13
- * @returns The transformed status code or undefined if no transformation was found or not an error.
10
+ * Map a Dataspace Protocol result to an HTTP status code.
11
+ * @param result The result to evaluate.
12
+ * @returns The HTTP status code if the result is a CatalogError or an error object, otherwise undefined.
14
13
  */
15
14
  export declare function transformErrorToStatusCode(result: unknown): HttpStatusCode | undefined;
package/docs/changelog.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-next.23](https://github.com/iotaledger/twin-federated-catalogue/compare/federated-catalogue-service-v0.0.3-next.22...federated-catalogue-service-v0.0.3-next.23) (2026-06-19)
4
+
5
+
6
+ ### Features
7
+
8
+ * add telemetry metrics to federated catalogue ([#93](https://github.com/iotaledger/twin-federated-catalogue/issues/93)) ([abd8965](https://github.com/iotaledger/twin-federated-catalogue/commit/abd89657d3991e6a50f8ed3845186069549df769))
9
+ * configurable timeout for mutex ([b2cd76b](https://github.com/iotaledger/twin-federated-catalogue/commit/b2cd76ba71008e558788eb3fc734b8f02eb7fdc8))
10
+
11
+
12
+ ### Dependencies
13
+
14
+ * The following workspace dependencies were updated
15
+ * dependencies
16
+ * @twin.org/federated-catalogue-models bumped from 0.0.3-next.22 to 0.0.3-next.23
17
+
18
+ ## [0.0.3-next.22](https://github.com/iotaledger/twin-federated-catalogue/compare/federated-catalogue-service-v0.0.3-next.21...federated-catalogue-service-v0.0.3-next.22) (2026-06-18)
19
+
20
+
21
+ ### Features
22
+
23
+ * remove hosting component ([#90](https://github.com/iotaledger/twin-federated-catalogue/issues/90)) ([801ef4f](https://github.com/iotaledger/twin-federated-catalogue/commit/801ef4fbc1033346089b9cc3cea6022ab57f4b01))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * use async getStore in tests ([1fe8654](https://github.com/iotaledger/twin-federated-catalogue/commit/1fe8654ecfb3437a4157fd63eb7fcb17bbee040b))
29
+
30
+
31
+ ### Dependencies
32
+
33
+ * The following workspace dependencies were updated
34
+ * dependencies
35
+ * @twin.org/federated-catalogue-models bumped from 0.0.3-next.21 to 0.0.3-next.22
36
+
3
37
  ## [0.0.3-next.21](https://github.com/iotaledger/twin-federated-catalogue/compare/federated-catalogue-service-v0.0.3-next.20...federated-catalogue-service-v0.0.3-next.21) (2026-06-12)
4
38
 
5
39
 
@@ -55,6 +55,24 @@ The class name of the component.
55
55
 
56
56
  ***
57
57
 
58
+ ### start() {#start}
59
+
60
+ > **start**(): `Promise`\<`void`\>
61
+
62
+ Register all federated catalogue metrics with the telemetry component.
63
+
64
+ #### Returns
65
+
66
+ `Promise`\<`void`\>
67
+
68
+ A promise that resolves when all metrics have been registered.
69
+
70
+ #### Implementation of
71
+
72
+ `IFederatedCatalogueComponent.start`
73
+
74
+ ***
75
+
58
76
  ### get() {#get}
59
77
 
60
78
  > **get**(`datasetId`, `trustPayload`): `Promise`\<`IDataspaceProtocolCatalogError` \| `IDcatDataset`\>
@@ -79,7 +97,7 @@ Optional payload for trust evaluation, if applicable.
79
97
 
80
98
  `Promise`\<`IDataspaceProtocolCatalogError` \| `IDcatDataset`\>
81
99
 
82
- The dataset if found, or a CatalogError if not found or an error occurs.
100
+ A promise that resolves with the dataset if found, or a CatalogError if not found or an error occurs.
83
101
 
84
102
  #### Implementation of
85
103
 
@@ -111,7 +129,7 @@ Optional payload for trust evaluation, if applicable.
111
129
 
112
130
  `Promise`\<`string` \| `IDataspaceProtocolCatalogError`\>
113
131
 
114
- The unique identifier of the stored dataset, or a CatalogError if an error occurs.
132
+ A promise that resolves with the unique identifier of the stored dataset, or a CatalogError if an error occurs.
115
133
 
116
134
  #### Implementation of
117
135
 
@@ -144,7 +162,7 @@ Optional payload for trust evaluation, if applicable.
144
162
 
145
163
  `Promise`\<`IDataspaceProtocolCatalogError` \| `undefined`\>
146
164
 
147
- Nothing, or a CatalogError if an error occurs.
165
+ A promise that resolves with undefined on success, or a CatalogError if removal fails.
148
166
 
149
167
  #### Implementation of
150
168
 
@@ -157,14 +175,14 @@ Nothing, or a CatalogError if an error occurs.
157
175
  > **query**(`filter`, `cursor`, `limit`, `trustPayload`): `Promise`\<\{ `result`: `IDataspaceProtocolCatalogError` \| `IDataspaceProtocolCatalog`; `cursor?`: `string`; \}\>
158
176
 
159
177
  Execute a query against the catalogue using registered filter plugins.
160
- Returns a DS Protocol compliant Catalog object with participantId.
178
+ Returns a Dataspace Protocol compliant Catalog object with participantId.
161
179
 
162
180
  The root catalog's participantId is the requesting participant (from context).
163
181
  Own datasets (matching requestingParticipantId) go directly in root dataset[].
164
182
  Other participants' datasets are grouped in nested catalog[] entries.
165
183
 
166
184
  For anonymous requests (no context), uses the first publisher found as fallback.
167
- Returns CatalogError 404 when no datasets exist, CatalogError 400 for invalid requests.
185
+ Returns a CatalogError with status 404 when no datasets exist, or status 400 for invalid requests.
168
186
 
169
187
  #### Parameters
170
188
 
@@ -172,7 +190,7 @@ Returns CatalogError 404 when no datasets exist, CatalogError 400 for invalid re
172
190
 
173
191
  `unknown`[] \| `undefined`
174
192
 
175
- The filter criteria containing @type, optional cursor and limit properties.
193
+ The filter criteria array, where the first element contains @type.
176
194
 
177
195
  ##### cursor
178
196
 
@@ -196,8 +214,7 @@ Optional payload for trust evaluation, if applicable.
196
214
 
197
215
  `Promise`\<\{ `result`: `IDataspaceProtocolCatalogError` \| `IDataspaceProtocolCatalog`; `cursor?`: `string`; \}\>
198
216
 
199
- Complete IDataspaceProtocolCatalog with @context, @id, @type, participantId, dataset/catalog,
200
- or CatalogError if validation fails or an error occurs.
217
+ A promise that resolves with the catalog result and optional next-page cursor.
201
218
 
202
219
  #### Implementation of
203
220
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **transformErrorToStatusCode**(`result`): `HttpStatusCode` \| `undefined`
4
4
 
5
- Transform the DS Protocol result to an HTTP status code.
5
+ Map a Dataspace Protocol result to an HTTP status code.
6
6
 
7
7
  ## Parameters
8
8
 
@@ -10,10 +10,10 @@ Transform the DS Protocol result to an HTTP status code.
10
10
 
11
11
  `unknown`
12
12
 
13
- The result to transform.
13
+ The result to evaluate.
14
14
 
15
15
  ## Returns
16
16
 
17
17
  `HttpStatusCode` \| `undefined`
18
18
 
19
- The transformed status code or undefined if no transformation was found or not an error.
19
+ The HTTP status code if the result is a CatalogError or an error object, otherwise undefined.
@@ -2,8 +2,7 @@
2
2
 
3
3
  > **transformToCatalogError**(`error`): `IDataspaceProtocolCatalogError`
4
4
 
5
- Transform an error to DS Protocol CatalogError format.
6
- Used by both service and route layers to ensure consistent error responses.
5
+ Transform an error to Dataspace Protocol CatalogError format.
7
6
 
8
7
  ## Parameters
9
8
 
@@ -17,4 +16,4 @@ The error to transform.
17
16
 
18
17
  `IDataspaceProtocolCatalogError`
19
18
 
20
- The CatalogError.
19
+ A CatalogError object with the error code and flattened reason array.
@@ -1,3 +1,11 @@
1
1
  # Interface: IFederatedCatalogueServiceConfig
2
2
 
3
3
  Configuration for the FederatedCatalogueService.
4
+
5
+ ## Properties
6
+
7
+ ### mutexTimeoutMs? {#mutextimeoutms}
8
+
9
+ > `optional` **mutexTimeoutMs?**: `number`
10
+
11
+ Timeout in milliseconds for acquiring a mutex lock, defaults to 5000ms.
@@ -40,6 +40,14 @@ trust
40
40
 
41
41
  ***
42
42
 
43
+ ### telemetryComponentType? {#telemetrycomponenttype}
44
+
45
+ > `optional` **telemetryComponentType?**: `string`
46
+
47
+ The component type for the optional telemetry component used for event metrics, defaults to no telemetry.
48
+
49
+ ***
50
+
43
51
  ### config? {#config}
44
52
 
45
53
  > `optional` **config?**: [`IFederatedCatalogueServiceConfig`](IFederatedCatalogueServiceConfig.md)
@@ -1,3 +1,5 @@
1
1
  # Variable: restEntryPoints
2
2
 
3
3
  > `const` **restEntryPoints**: `IRestRouteEntryPoint`[]
4
+
5
+ REST route entry points for the federated catalogue service.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/federated-catalogue-service",
3
- "version": "0.0.3-next.21",
3
+ "version": "0.0.3-next.23",
4
4
  "description": "Federated Catalogue contract implementation and REST endpoint definitions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,13 +21,14 @@
21
21
  "@twin.org/data-json-ld": "next",
22
22
  "@twin.org/entity": "next",
23
23
  "@twin.org/entity-storage-models": "next",
24
- "@twin.org/federated-catalogue-models": "0.0.3-next.21",
24
+ "@twin.org/federated-catalogue-models": "0.0.3-next.23",
25
25
  "@twin.org/logging-models": "next",
26
26
  "@twin.org/nameof": "next",
27
27
  "@twin.org/standards-dataspace-protocol": "next",
28
28
  "@twin.org/standards-dublin-core": "next",
29
29
  "@twin.org/standards-foaf": "next",
30
30
  "@twin.org/standards-w3c-dcat": "next",
31
+ "@twin.org/telemetry-models": "next",
31
32
  "@twin.org/trust-models": "next",
32
33
  "@twin.org/vault-models": "next",
33
34
  "@twin.org/web": "next"