#!/usr/bin/env node import { createHash } from "node:crypto"; import { readFile, writeFile } from "node:fs/promises"; import { resolve } from "node:path"; import { compileL2ExecutionPlan, instantiateL2Connection, } from "../../packages/external-provider-contract/src/index.mjs"; import { geliosProviderPackageV10, geliosTelemetryFieldRegistryV3, } from "../../packages/external-provider-contract/providers/gelios/v10/index.mjs"; const engineRoot = resolve( process.env.NODEDC_ENGINE_SOURCE_ROOT || "../NODEDC_ENGINE_INFRA", ); const executionCatalogPath = resolve( engineRoot, "nodedc-source/server/assets/execution-plans/v1/catalog.json", ); const securityCatalogPath = resolve( engineRoot, "nodedc-source/server/assets/provider-packages/v1/catalog.json", ); const executionCatalog = JSON.parse( await readFile(executionCatalogPath, "utf8"), ); executionCatalog.runtime.compilerVersions = [ ...new Set([...executionCatalog.runtime.compilerVersions, "1.3.0"]), ].sort(); executionCatalog.packages = executionCatalog.packages.filter( (providerPackage) => providerPackage.id !== geliosProviderPackageV10.id, ); executionCatalog.packages.push(executionCatalogEntry()); await writeFile(executionCatalogPath, `${JSON.stringify(executionCatalog, null, 2)}\n`); const securityCatalog = JSON.parse( await readFile(securityCatalogPath, "utf8"), ); securityCatalog.packages = securityCatalog.packages.filter( (providerPackage) => providerPackage.id !== geliosProviderPackageV10.id, ); securityCatalog.packages.push(securityCatalogEntry()); await writeFile(securityCatalogPath, `${JSON.stringify(securityCatalog, null, 2)}\n`); console.log(JSON.stringify({ ok: true, providerPackage: geliosProviderPackageV10.id, executionCatalogPath, securityCatalogPath, }, null, 2)); function executionCatalogEntry() { const profiles = geliosProviderPackageV10.collectionProfiles.map((profile) => { const connection = instantiateL2Connection(geliosProviderPackageV10, { tenantId: "tenant-catalog-build", connectionId: `catalog-${profile.id.replaceAll(".", "-")}`, collectionProfileId: profile.id, providerCredentialRef: "ndc-credref:catalog-build-provider-v10", }); const plan = compileL2ExecutionPlan( geliosProviderPackageV10, connection, profile.dataProductId === "fleet.positions.current.v5" ? { telemetryFieldRegistry: geliosTelemetryFieldRegistryV3 } : {}, ); const { packageDigest: _packageDigest, ...artifacts } = plan.artifacts; return { id: profile.id, dataProductId: profile.dataProductId, capabilityIds: [...profile.capabilityIds], stepSignatures: plan.steps.map((step) => ({ id: step.id, kind: step.kind, ...(step.config.capabilityId ? { capabilityId: step.config.capabilityId } : {}), ...(step.config.mappingContractId ? { mappingContractId: step.config.mappingContractId, } : {}), ...(step.config.dataProductId ? { dataProductId: step.config.dataProductId } : {}), ...(step.config.nodeType ? { nodeType: step.config.nodeType } : {}), })), artifacts, }; }); return { id: geliosProviderPackageV10.id, providerId: geliosProviderPackageV10.providerId, version: geliosProviderPackageV10.version, contractDigest: canonicalDigest(geliosProviderPackageV10), providerCredential: { authModeId: "gelios.rest-rotating-bearer.v3", credentialType: "ndcProviderRotatingAccessApi", }, publisher: { nodeType: "n8n-nodes-ndc.ndcDataProductPublish", credentialType: "ndcDataProductWriterApi", }, capabilities: geliosProviderPackageV10.capabilities.map((capability) => ({ id: capability.id, contractDigest: canonicalDigest(capability), requestDigest: canonicalDigest(capability.request), method: capability.request.method, url: requestUrl(capability.request), })), profiles, }; } function securityCatalogEntry() { const productsByCapability = new Map( geliosProviderPackageV10.capabilities.map((capability) => [capability.id, new Set()]), ); for (const profile of geliosProviderPackageV10.collectionProfiles) { if (profile.dataProductId !== "fleet.units.contacts.current.v1") continue; for (const capabilityId of profile.capabilityIds) { productsByCapability.get(capabilityId)?.add(profile.dataProductId); } } return { id: geliosProviderPackageV10.id, version: geliosProviderPackageV10.version, providerId: geliosProviderPackageV10.providerId, providerCredential: { authModeId: "gelios.rest-rotating-bearer.v3", credentialType: "ndcProviderRotatingAccessApi", }, capabilities: geliosProviderPackageV10.capabilities .filter((capability) => productsByCapability.get(capability.id)?.size) .map((capability) => ({ id: capability.id, classification: capability.classification, status: capability.status, request: { method: capability.request.method, url: requestUrl(capability.request), }, dataProductIds: [...productsByCapability.get(capability.id)].sort(), })), publisher: { nodeType: "n8n-nodes-ndc.ndcDataProductPublish", credentialType: "ndcDataProductWriterApi", }, }; } function requestUrl(request) { const url = new URL(request.path, request.baseUrl); for (const [name, value] of Object.entries(request.query || {})) { url.searchParams.append(name, String(value)); } return url.toString(); } function canonicalDigest(value) { return `sha256:${createHash("sha256").update(JSON.stringify(stableValue(value)), "utf8").digest("hex")}`; } function stableValue(value) { if (Array.isArray(value)) return value.map(stableValue); if (!value || typeof value !== "object") return value; return Object.fromEntries( Object.keys(value).sort().map((key) => [key, stableValue(value[key])]), ); }