UNPKG

@dgac/nmb2b-client

Version:

EUROCONTROL Network Manager B2B SOAP client

1 lines 109 kB
{"version":3,"sources":["../src/index.ts","../src/security.ts","../src/utils/debug.ts","../src/constants.ts","../src/config.ts","../src/utils/xsd/index.ts","../src/utils/fs.ts","../src/utils/xsd/downloadFile.ts","../src/utils/xsd/createAxiosConfig.ts","../src/utils/xsd/filePath.ts","../src/utils/timeFormats.ts","../src/Airspace/index.ts","../src/utils/transformers/types.ts","../src/utils/transformers/serializer.ts","../src/utils/transformers/index.ts","../src/utils/instrumentation/withLog.ts","../src/utils/instrumentation/index.ts","../src/utils/NMB2BError.ts","../src/utils/internals.ts","../src/Airspace/queryCompleteAIXMDatasets.ts","../src/Airspace/retrieveAUP.ts","../src/Airspace/retrieveAUPChain.ts","../src/Airspace/retrieveEAUPChain.ts","../src/Flight/index.ts","../src/Flight/retrieveFlight.ts","../src/Flight/queryFlightsByAirspace.ts","../src/Flight/queryFlightPlans.ts","../src/Flight/queryFlightsByTrafficVolume.ts","../src/Flight/queryFlightsByMeasure.ts","../src/Flight/queryFlightsByAerodrome.ts","../src/Flight/queryFlightsByAerodromeSet.ts","../src/Flight/queryFlightsByAircraftOperator.ts","../src/Flow/index.ts","../src/Flow/queryHotspots.ts","../src/Flow/queryRegulations.ts","../src/Flow/queryTrafficCountsByAirspace.ts","../src/Flow/queryTrafficCountsByTrafficVolume.ts","../src/Flow/retrieveCapacityPlan.ts","../src/Flow/retrieveOTMVPlan.ts","../src/Flow/retrieveRunwayConfigurationPlan.ts","../src/Flow/retrieveSectorConfigurationPlan.ts","../src/Flow/updateCapacityPlan.ts","../src/Flow/updateOTMVPlan.ts","../src/GeneralInformation/index.ts","../src/GeneralInformation/queryNMB2BWSDLs.ts","../src/GeneralInformation/retrieveUserinformation.ts"],"sourcesContent":["import { isConfigValid, obfuscate } from './config';\nimport type { B2BFlavour } from './constants';\nimport type { Security } from './security';\nimport d from './utils/debug';\nimport { download as downloadWSDL } from './utils/xsd';\nconst debug = d();\n\nimport type { AirspaceService } from './Airspace';\nimport { getAirspaceClient } from './Airspace';\nimport type { FlightService } from './Flight';\nimport { getFlightClient } from './Flight';\n\nimport type { FlowService } from './Flow';\nimport { getFlowClient } from './Flow';\nimport type { GeneralInformationService } from './GeneralInformation';\nimport { getGeneralInformationClient } from './GeneralInformation';\n\nexport { AirspaceService } from './Airspace';\nexport { FlightService } from './Flight';\nexport { FlowService } from './Flow';\nexport { GeneralInformationService } from './GeneralInformation';\nexport { NMB2BError } from './utils/NMB2BError';\n\nimport type { SoapDeserializer } from './utils/types';\n\nexport type SafeB2BDeserializedResponse<TResponsePart> =\n SoapDeserializer<TResponsePart>;\n\nexport interface B2BClient {\n Airspace: AirspaceService;\n Flight: FlightService;\n Flow: FlowService;\n GeneralInformation: GeneralInformationService;\n}\n\ninterface InputOptions {\n security: Security;\n flavour?: B2BFlavour;\n XSD_PATH?: string;\n endpoint?: string;\n xsdEndpoint?: string;\n ignoreWSDLCache?: boolean;\n}\n\nconst defaults = {\n flavour: 'OPS' as B2BFlavour,\n XSD_PATH: '/tmp/b2b-xsd',\n};\n\nexport async function makeB2BClient(args: InputOptions): Promise<B2BClient> {\n const options = { ...defaults, ...args };\n\n debug('Instantiating B2B Client ...');\n if (!isConfigValid(options)) {\n debug('Invalid options provided');\n throw new Error('Invalid options provided');\n }\n\n debug('Config is %o', obfuscate(options));\n\n await downloadWSDL(options);\n\n return Promise.all([\n getAirspaceClient(options),\n getFlightClient(options),\n getFlowClient(options),\n getGeneralInformationClient(options),\n ])\n .then((res) => {\n debug('Successfully created B2B Client');\n return res;\n })\n .then(([Airspace, Flight, Flow, GeneralInformation]) => ({\n Airspace,\n Flight,\n Flow,\n GeneralInformation,\n }));\n}\n\nexport async function makeAirspaceClient(\n args: InputOptions,\n): Promise<AirspaceService> {\n const options = { ...defaults, ...args };\n debug('Instantiating B2B Airspace client ...');\n\n if (!isConfigValid(options)) {\n debug('Invalid options provided');\n throw new Error('Invalid options provided');\n }\n\n debug('Config is %o', obfuscate(options));\n\n await downloadWSDL(options);\n\n return getAirspaceClient(options).then((res) => {\n debug('Successfully created B2B Airspace client');\n return res;\n });\n}\n\nexport async function makeFlightClient(\n args: InputOptions,\n): Promise<FlightService> {\n const options = { ...defaults, ...args };\n debug('Instantiating B2B Flight client ...');\n\n if (!isConfigValid(options)) {\n debug('Invalid options provided');\n throw new Error('Invalid options provided');\n }\n\n debug('Config is %o', obfuscate(options));\n\n await downloadWSDL(options);\n\n return getFlightClient(options).then((res) => {\n debug('Successfully created B2B Flight client');\n return res;\n });\n}\n\nexport async function makeFlowClient(args: InputOptions): Promise<FlowService> {\n const options = { ...defaults, ...args };\n debug('Instantiating B2B Flow client ...');\n\n if (!isConfigValid(options)) {\n debug('Invalid options provided');\n throw new Error('Invalid options provided');\n }\n\n debug('Config is %o', obfuscate(options));\n\n await downloadWSDL(options);\n\n return getFlowClient(options).then((res) => {\n debug('Successfully created B2B Flow client');\n return res;\n });\n}\n\nexport async function makeGeneralInformationClient(\n args: InputOptions,\n): Promise<GeneralInformationService> {\n const options = { ...defaults, ...args };\n debug('Instantiating B2B GeneralInformation client ...');\n\n if (!isConfigValid(options)) {\n debug('Invalid options provided');\n throw new Error('Invalid options provided');\n }\n\n debug('Config is %o', obfuscate(options));\n\n await downloadWSDL(options);\n\n return getGeneralInformationClient(options).then((res) => {\n debug('Successfully created B2B GeneralInformation client');\n return res;\n });\n}\n","import invariant from 'invariant';\nimport d from './utils/debug';\nconst debug = d('security');\nimport type { Config } from './config';\nimport type { ISecurity } from 'soap';\nimport {\n ClientSSLSecurity,\n ClientSSLSecurityPFX,\n BasicAuthSecurity,\n} from 'soap';\nimport fs from 'fs';\n\ninterface PfxSecurity {\n pfx: Buffer;\n passphrase: string;\n}\n\ninterface PemSecurity {\n cert: Buffer;\n key: Buffer;\n passphrase?: string;\n}\n\ninterface ApiGwSecurity {\n apiKeyId: string;\n apiSecretKey: string;\n}\n\nexport type Security = PfxSecurity | PemSecurity | ApiGwSecurity;\n\nexport function isValidSecurity(obj: unknown): obj is Security {\n invariant(!!obj && typeof obj === 'object', 'Must be an object');\n\n if ('apiKeyId' in obj) {\n invariant(\n !!obj.apiKeyId &&\n typeof obj.apiKeyId === 'string' &&\n obj.apiKeyId.length > 0,\n 'security.apiKeyId must be a string with a length > 0',\n );\n\n invariant(\n 'apiSecretKey' in obj &&\n typeof obj.apiSecretKey === 'string' &&\n obj.apiSecretKey.length > 0,\n 'security.apiSecretKey must be defined when using security.apiKeyId',\n );\n\n return true;\n }\n\n invariant(\n ('pfx' in obj && Buffer.isBuffer(obj.pfx)) ||\n ('cert' in obj && Buffer.isBuffer(obj.cert)),\n 'security.pfx or security.cert must be buffers',\n );\n\n if ('cert' in obj && obj.cert) {\n invariant(\n 'key' in obj && obj.key && Buffer.isBuffer(obj.key),\n 'security.key must be a buffer if security.pem is defined',\n );\n }\n\n return true;\n}\n\nexport function prepareSecurity(config: Config): ISecurity {\n const { security } = config;\n\n if ('apiKeyId' in security) {\n const { apiKeyId, apiSecretKey } = security;\n debug('Using ApiGateway security');\n return new BasicAuthSecurity(apiKeyId, apiSecretKey);\n } else if ('pfx' in security) {\n const { pfx, passphrase } = security;\n debug('Using PFX certificates');\n return new ClientSSLSecurityPFX(pfx, passphrase);\n } else if ('cert' in security) {\n debug('Using PEM certificates');\n const { key, cert, passphrase } = security;\n return new ClientSSLSecurity(\n key,\n cert,\n undefined,\n passphrase ? { passphrase } : null,\n );\n }\n\n throw new Error('Invalid security object');\n}\n\nlet envSecurity: Security | undefined;\n\n/**\n * Create a security objet from environment variables\n *\n * Will cache data for future use.\n *\n * @returns Security configuration\n */\nexport function fromEnv(): Security {\n if (envSecurity) {\n return envSecurity;\n }\n\n const { B2B_CERT, B2B_API_KEY_ID, B2B_API_SECRET_KEY } = process.env;\n\n if (!B2B_CERT && !B2B_API_KEY_ID) {\n throw new Error(\n 'Please define a B2B_CERT or a B2B_API_KEY_ID environment variable',\n );\n }\n\n if (B2B_API_KEY_ID) {\n if (!B2B_API_SECRET_KEY) {\n throw new Error(\n `When using B2B_API_KEY_ID, a B2B_API_SECRET_KEY must be defined`,\n );\n }\n\n return {\n apiKeyId: B2B_API_KEY_ID,\n apiSecretKey: B2B_API_SECRET_KEY,\n };\n }\n\n if (!B2B_CERT) {\n throw new Error('Should never happen');\n }\n\n if (!fs.existsSync(B2B_CERT)) {\n throw new Error(`${B2B_CERT} is not a valid certificate file`);\n }\n\n const pfxOrPem = fs.readFileSync(B2B_CERT);\n\n if (!process.env.B2B_CERT_FORMAT || process.env.B2B_CERT_FORMAT === 'pfx') {\n envSecurity = {\n pfx: pfxOrPem,\n passphrase: process.env.B2B_CERT_PASSPHRASE ?? '',\n };\n\n return envSecurity;\n } else if (process.env.B2B_CERT_FORMAT === 'pem') {\n if (!process.env.B2B_CERT_KEY || !fs.existsSync(process.env.B2B_CERT_KEY)) {\n throw new Error(\n 'Please define a valid B2B_CERT_KEY environment variable',\n );\n }\n\n envSecurity = {\n cert: pfxOrPem,\n key: fs.readFileSync(process.env.B2B_CERT_KEY),\n };\n\n if (process.env.B2B_CERT_PASSPHRASE) {\n envSecurity = {\n ...envSecurity,\n passphrase: process.env.B2B_CERT_PASSPHRASE,\n };\n return envSecurity;\n }\n\n return envSecurity;\n }\n\n throw new Error('Unsupported B2B_CERT_FORMAT, must be pfx or pem');\n}\n\n/**\n * Convenience function to clear the cached security objet\n */\nexport function clearCache(): void {\n envSecurity = undefined;\n}\n","import d from 'debug';\nconst PREFIX = '@dgac/nmb2b-client';\nconst debug = d(PREFIX);\n\nfunction log(ns?: string) {\n if (!ns) {\n return debug;\n }\n\n return debug.extend(ns);\n}\n\nexport default log;\n","import path from 'path';\nexport type B2BFlavour = 'OPS' | 'PREOPS';\n\nexport const B2B_VERSION = '27.0.0';\nexport const B2BFlavours = ['OPS', 'PREOPS'];\n\nexport const getWSDLPath = ({\n service,\n flavour,\n XSD_PATH,\n}: {\n service: string;\n flavour: B2BFlavour;\n XSD_PATH: string;\n}): string =>\n path.join(\n XSD_PATH,\n `${B2B_VERSION}/${service}_${flavour}_${B2B_VERSION}.wsdl`,\n );\n","import type { Security } from './security';\nimport { isValidSecurity } from './security';\nimport type { B2BFlavour } from './constants';\nimport { B2B_VERSION, B2BFlavours } from './constants';\nimport invariant from 'invariant';\nimport { URL } from 'url';\nimport type { Client as SoapClient } from 'soap';\n\nexport interface Config {\n endpoint?: string;\n xsdEndpoint?: string;\n ignoreWSDLCache?: boolean;\n security: Security;\n flavour: B2BFlavour;\n XSD_PATH: string;\n soapClient?: null | SoapClient;\n}\n\nexport function isConfigValid(args: unknown): args is Config {\n invariant(!!args && typeof args === 'object', 'Invalid config');\n\n invariant(\n 'security' in args && isValidSecurity(args.security),\n 'Please provide a valid security option',\n );\n\n invariant(\n 'flavour' in args && typeof args.flavour === 'string',\n `Invalid config.flavour. Supported flavours: ${B2BFlavours.join(', ')}`,\n );\n\n invariant(\n B2BFlavours.includes(args.flavour),\n `Invalid config.flavour. Supported flavours: ${B2BFlavours.join(', ')}`,\n );\n\n if ('apiKeyId' in args.security) {\n invariant(\n 'endpoint' in args && !!args.endpoint,\n `When using an config.security.apiKeyId, config.endpoint must be defined`,\n );\n\n invariant(\n 'xsdEndpoint' in args && !!args.xsdEndpoint,\n `When using an config.security.apiKeyId, config.xsdEndpoint must be defined`,\n );\n }\n\n return true;\n}\n\nconst B2B_ROOTS = {\n OPS: 'https://www.b2b.nm.eurocontrol.int',\n PREOPS: 'https://www.b2b.preops.nm.eurocontrol.int',\n};\n\nexport function getEndpoint(\n config: { endpoint?: string; flavour?: B2BFlavour } = {},\n): string {\n const { endpoint, flavour } = config;\n\n if (flavour && flavour === 'PREOPS') {\n return `${\n endpoint ?? B2B_ROOTS.PREOPS\n }/B2B_PREOPS/gateway/spec/${B2B_VERSION}`;\n }\n\n return `${endpoint ?? B2B_ROOTS.OPS}/B2B_OPS/gateway/spec/${B2B_VERSION}`;\n}\n\nexport function getFileEndpoint(\n config: { endpoint?: string; flavour?: B2BFlavour } = {},\n): string {\n const { endpoint, flavour } = config;\n\n if (flavour && flavour === 'PREOPS') {\n return `${endpoint ?? B2B_ROOTS.PREOPS}/FILE_PREOPS/gateway/spec`;\n }\n\n return `${endpoint ?? B2B_ROOTS.OPS}/FILE_OPS/gateway/spec`;\n}\n\nexport function getFileUrl(\n path: string,\n config: { flavour?: B2BFlavour; endpoint?: string } = {},\n): string {\n if (config.endpoint) {\n return new URL(\n (path[0] && path.startsWith('/') ? '' : '/') + path,\n config.endpoint,\n ).toString();\n }\n\n return (\n getFileEndpoint(config) +\n (path[0] && path.startsWith('/') ? '' : '/') +\n path\n );\n}\n\nexport function obfuscate(config: Config) {\n return {\n ...config,\n security: Object.keys(config.security).reduce((prev, curr) => {\n return {\n ...prev,\n [curr]: 'xxxxxxxxxxxxxxxx',\n };\n }, {}),\n };\n}\n","import fs from 'fs';\nimport path from 'path';\nimport lockfile from 'proper-lockfile';\nimport { promisify } from 'util';\nimport type { Config } from '../../config';\nimport { B2B_VERSION } from '../../constants';\nimport d from '../debug';\nimport { createDir, dirExists } from '../fs';\nimport { downloadFile } from './downloadFile';\nimport { requestFilename } from './filePath';\nconst debug = d('wsdl');\n\nconst readdir = promisify(fs.readdir);\n\nconst getWSDLPath = ({ XSD_PATH }: Config) => path.join(XSD_PATH, B2B_VERSION);\n\nasync function WSDLExists(config: Config): Promise<boolean> {\n const directory = getWSDLPath(config);\n\n debug(`Checking if directory ${directory} exists`);\n\n if (!(await dirExists(directory))) {\n return false;\n }\n\n const files = await readdir(directory);\n return files.length > 0;\n}\n\nexport async function download(config: Config): Promise<void> {\n const outputDir = getWSDLPath(config);\n\n if (!(await dirExists(outputDir))) {\n debug(`Creating directory ${outputDir}`);\n await createDir(outputDir);\n }\n\n debug(`Acquiring lock for folder ${outputDir}`);\n const release = await lockfile.lock(outputDir, {\n retries: 5,\n });\n\n debug(`Lock acquired. Testing WSDL existence ...`);\n\n const hasWSDL = await WSDLExists(config);\n\n if (!config.ignoreWSDLCache && hasWSDL) {\n debug('WSDL found');\n await release();\n return;\n }\n\n const fileName = await requestFilename(config);\n\n debug(`Downloading ${fileName}`);\n\n await downloadFile(fileName, config);\n await release();\n}\n","import * as fs from 'fs/promises';\nimport d from './debug';\nconst debug = d('dir-exists');\n\nexport async function dirExists(\n path: string,\n { readable, writable }: { readable: boolean; writable?: boolean } = {\n readable: true,\n writable: false,\n },\n): Promise<boolean> {\n debug(\n `Testing if directory ${path} is readable ${writable ? 'and writable ' : ''}...`,\n );\n\n try {\n const stats = await fs.stat(path);\n if (!stats.isDirectory()) {\n // Directory does not exists\n return false;\n }\n\n // Check that the directory is writable and readable\n await fs.access(\n path,\n (writable ? fs.constants.W_OK : 0) | (readable ? fs.constants.R_OK : 0),\n );\n\n debug(`Directory ${path} is accessible`);\n\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function createDir(path: string): Promise<void> {\n debug('Creating directory %s ...', path);\n await fs.mkdir(path, { recursive: true });\n}\n","import axios from 'axios';\nimport { extract } from 'tar';\nimport { getFileUrl } from '../../config';\nimport type { B2BFlavour } from '../../constants';\nimport type { Security } from '../../security';\nimport d from '../debug';\nimport { createAxiosConfig } from './createAxiosConfig';\nimport type { Readable } from 'stream';\nconst debug = d('wsdl-downloader');\n\nexport async function downloadFile(\n filePath: string,\n {\n flavour,\n security,\n XSD_PATH: outputDir,\n xsdEndpoint,\n }: {\n flavour: B2BFlavour;\n security?: Security;\n XSD_PATH: string;\n xsdEndpoint?: string;\n },\n): Promise<void> {\n const options = createAxiosConfig({ security });\n\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n const url = xsdEndpoint || getFileUrl(filePath, { flavour });\n\n debug(`Downloading ${url}`);\n try {\n const res = await axios.get<Readable>(url, {\n timeout: 15 * 1000,\n responseType: 'stream',\n ...options,\n });\n\n return new Promise((resolve, reject) => {\n res.data\n .pipe(extract({ cwd: outputDir }))\n .on('error', reject)\n .on('close', () => {\n debug('Downloaded and extracted WSDL files');\n resolve();\n });\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Unknown error';\n throw new Error(`Unable to download WSDL: ${message}`, { cause: err });\n }\n}\n","import type { Security } from '../../security';\nimport https from 'https';\n\nexport function createAxiosConfig({ security }: { security?: Security }) {\n if (!!security && 'apiKeyId' in security) {\n return {\n auth: {\n username: security.apiKeyId,\n password: security.apiSecretKey,\n },\n };\n }\n\n const httpsAgent = new https.Agent(security);\n\n return { httpsAgent };\n}\n","import { UTCDateMini } from '@date-fns/utc';\nimport axios from 'axios';\nimport { format } from 'date-fns';\nimport { getEndpoint } from '../../config';\nimport type { B2BFlavour } from '../../constants';\nimport { B2B_VERSION } from '../../constants';\nimport type { Security } from '../../security';\nimport { timeFormatWithSeconds } from '../timeFormats';\nimport { createAxiosConfig } from './createAxiosConfig';\n\nconst makeQuery = ({ version }: { version: string }) => `\n<soap:Envelope\n xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"\n xmlns:generalinformation=\"eurocontrol/cfmu/b2b/GeneralinformationServices\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n>\n <soap:Header />\n <soap:Body>\n <generalinformation:NMB2BWSDLsRequest>\n <sendTime>${format(new UTCDateMini(), timeFormatWithSeconds)}</sendTime>\n <version>${version}</version>\n </generalinformation:NMB2BWSDLsRequest>\n </soap:Body>\n</soap:Envelope>\n`;\n\nexport async function requestFilename({\n flavour,\n security,\n xsdEndpoint,\n}: {\n flavour: B2BFlavour;\n security: Security;\n xsdEndpoint?: string;\n}): Promise<string> {\n if (xsdEndpoint) {\n return xsdEndpoint;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!!security && 'apiKeyId' in security) {\n throw new Error(\n 'Should never happen, config.xsdEndpoint should be defined',\n );\n }\n\n const res = await axios<string>({\n url: getEndpoint({ flavour }),\n method: 'POST',\n data: makeQuery({ version: B2B_VERSION }),\n responseType: 'text',\n ...createAxiosConfig({ security }),\n });\n\n const matches = /<id>(.+)<\\/id>/.exec(res.data);\n\n if (!matches?.[1]) {\n throw new Error(`Could not extract WSDL tarball file from B2B response`);\n }\n\n return matches[1];\n}\n","export const timeFormat = 'yyyy-MM-dd HH:mm';\nexport const dateFormat = 'yyyy-MM-dd';\nexport const timeFormatWithSeconds = timeFormat + ':ss';\n","import { createClient, type Client as SoapClient } from 'soap';\nimport type { Config } from '../config';\nimport { getWSDLPath } from '../constants';\nimport { prepareSecurity } from '../security';\nimport { deserializer as customDeserializer } from '../utils/transformers';\n\nexport const getWSDL = ({\n XSD_PATH,\n flavour,\n}: Pick<Config, 'flavour' | 'XSD_PATH'>) =>\n getWSDLPath({ service: 'AirspaceServices', flavour, XSD_PATH });\n\nexport type AirspaceClient = SoapClient;\n\nfunction createAirspaceServices(config: Config): Promise<AirspaceClient> {\n const WSDL = getWSDL(config);\n const security = prepareSecurity(config);\n\n return new Promise((resolve, reject) => {\n createClient(WSDL, { customDeserializer }, (err, client) => {\n try {\n if (err) {\n reject(\n err instanceof Error\n ? err\n : new Error('Unknown error', { cause: err }),\n );\n return;\n }\n\n client.setSecurity(security);\n\n resolve(client);\n } catch (err) {\n // TODO: Implement a proper debug log message output\n console.log(err);\n reject(\n err instanceof Error\n ? err\n : new Error('Unknown error', { cause: err }),\n );\n return;\n }\n });\n });\n}\n\nimport type { Resolver as QueryCompleteAIXMDatasets } from './queryCompleteAIXMDatasets';\nimport queryCompleteAIXMDatasets from './queryCompleteAIXMDatasets';\nimport type { Resolver as RetrieveAUP } from './retrieveAUP';\nimport retrieveAUP from './retrieveAUP';\nimport type { Resolver as RetrieveAUPChain } from './retrieveAUPChain';\nimport retrieveAUPChain from './retrieveAUPChain';\nimport type { Resolver as RetrieveEAUPChain } from './retrieveEAUPChain';\nimport retrieveEAUPChain from './retrieveEAUPChain';\n\nimport type { BaseServiceInterface } from '../Common/ServiceInterface';\n\nexport interface AirspaceService extends BaseServiceInterface {\n queryCompleteAIXMDatasets: QueryCompleteAIXMDatasets;\n retrieveAUPChain: RetrieveAUPChain;\n retrieveEAUPChain: RetrieveEAUPChain;\n retrieveAUP: RetrieveAUP;\n}\n\nexport function getAirspaceClient(config: Config): Promise<AirspaceService> {\n return createAirspaceServices(config).then((client) => ({\n __soapClient: client,\n config,\n queryCompleteAIXMDatasets: queryCompleteAIXMDatasets(client),\n retrieveAUPChain: retrieveAUPChain(client),\n retrieveEAUPChain: retrieveEAUPChain(client),\n retrieveAUP: retrieveAUP(client),\n }));\n}\n","import { UTCDate } from '@date-fns/utc';\nimport { format } from 'date-fns';\nimport * as timeFormats from '../timeFormats';\n\nconst outputBase = {\n integer: (text: string) => {\n return parseInt(text, 10);\n },\n /**\n *\n * Parse a NMB2B date/datetime.\n *\n * All datetimes are assumed to be UTC.\n *\n * Per NM B2B documentation, we only need to support these formats:\n * - DateTimeMinute: YYYY-MM-DD hh:mm\n * - DateTimeSecond: YYYY-MM-DD hh:mm:ss\n * - DateYearMonthDay: YYYY-MM-DD\n *\n * All dates are\n * @param text NM B2B Date string\n * @returns Parsed Date instance\n */\n date: (text: string) => {\n // eslint-disable-next-line prefer-const\n let [date, time] = text.split(' ');\n\n if (!time) {\n return new Date(text);\n }\n\n if (time.length === 5) {\n time += ':00';\n }\n\n return new Date(`${date}T${time}Z`);\n },\n};\n\ninterface SerDe {\n [key: string]: {\n input: null | ((input: any, ctx?: any) => any);\n output: null | ((input: any, ctx?: any) => any);\n };\n}\n\nexport const types = {\n FlightLevel_DataType: {\n input: null,\n output: outputBase.integer,\n },\n DurationHourMinute: {\n input: (d: number): string => {\n const totalMinutes = Math.floor(d / 60);\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n return `${hours}`.padStart(2, '0') + `${minutes}`.padStart(2, '0');\n },\n output: (s: string): number => {\n const hours = parseInt(s.slice(0, 2), 10);\n const minutes = parseInt(s.slice(2), 10);\n\n return 60 * (60 * hours + minutes);\n },\n },\n DurationHourMinuteSecond: {\n input: (d: number): string => {\n const totalMinutes = Math.floor(d / 60);\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n return (\n `${hours}`.padStart(2, '0') +\n `${minutes}`.padStart(2, '0') +\n `${d % 60}`.padStart(2, '0')\n );\n },\n output: (s: string): number => {\n const hours = parseInt(s.slice(0, 2), 10);\n const minutes = parseInt(s.slice(2, 4), 10);\n const seconds = parseInt(s.slice(4), 10);\n\n return 3600 * hours + 60 * minutes + seconds;\n },\n },\n DurationMinute: {\n input: (d: number): number => Math.floor(d / 60),\n output: (d: number): number => 60 * d,\n },\n CountsValue: {\n input: null,\n output: outputBase.integer,\n },\n DateTimeMinute: {\n input: (d: Date): string => format(new UTCDate(d), timeFormats.timeFormat),\n output: outputBase.date,\n },\n DateYearMonthDay: {\n input: (d: Date): string => format(new UTCDate(d), timeFormats.dateFormat),\n output: outputBase.date,\n },\n DateTimeSecond: {\n input: (d: Date): string =>\n format(new UTCDate(d), timeFormats.timeFormatWithSeconds),\n output: outputBase.date,\n },\n DistanceNM: {\n input: null,\n output: outputBase.integer,\n },\n DistanceM: {\n input: null,\n output: outputBase.integer,\n },\n Bearing: {\n input: null,\n output: outputBase.integer,\n },\n OTMVThreshold: {\n input: null,\n output: outputBase.integer,\n },\n} satisfies SerDe;\n","/* eslint-disable @typescript-eslint/no-unsafe-return */\n/* eslint-disable @typescript-eslint/no-unsafe-argument */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\nimport { types } from './types';\nimport { piped, identity, evolve, map } from 'remeda';\n\nexport function prepareSerializer<T>(schema: any): (input: T) => T {\n const transformer = prepareTransformer(schema);\n return piped(\n reorderKeys(schema),\n transformer ? evolve(transformer) : identity,\n // (obj) => {\n // console.log(JSON.stringify(obj, null, 2));\n // return obj;\n // },\n ) as any as (input: T) => T;\n}\n\nfunction reduceXSDType(str: string): string {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return str.split('|')[0]!;\n}\n\ninterface Schema {\n [k: string]: string | Schema;\n}\n\ninterface Transformer {\n // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\n [k: string]: (input: any) => any | Transformer;\n}\n\nfunction prepareTransformer(schema: Schema): null | Transformer {\n return Object.keys(schema).reduce((prev: null | Transformer, curr) => {\n let key = curr;\n let isArray = false;\n\n /**\n * If the current key marks an array, we need to map over the values instead of just trying\n * to transform the value.\n *\n * We also need to assign the correct key to the transformer.\n */\n if (curr.endsWith('[]')) {\n key = curr.slice(0, -2);\n isArray = true;\n }\n\n if (typeof schema[curr] === 'string') {\n const type = reduceXSDType(schema[curr]);\n\n if ((types as any)[type]?.input) {\n const transformer = (types as any)[type].input;\n return { ...prev, [key]: isArray ? map(transformer) : transformer };\n }\n } else if (typeof schema[curr] === 'object') {\n const subItem = prepareTransformer(schema[curr]);\n if (subItem) {\n return {\n ...prev,\n [key]: isArray ? map(evolve(subItem)) : subItem,\n };\n }\n }\n\n return prev;\n }, null);\n}\n\nexport function reorderKeys<T extends Schema, O extends { [key: string]: any }>(\n schema: T,\n): (obj: O) => O {\n return (obj: O): O => {\n // console.log(JSON.stringify(schema, null, 2));\n // console.log(JSON.stringify(obj, null, 2));\n\n // Loop through schema, pull property from Object\n return Object.keys(schema).reduce<any>((prev, curr) => {\n const lookupKey: string = curr.replace(/\\[\\]$/, '');\n const isArrayExpected: boolean = curr.endsWith('[]');\n\n if (!(lookupKey in obj)) {\n return prev;\n }\n\n const currSchema = schema[curr];\n\n if (typeof currSchema === 'string') {\n prev[lookupKey] = obj[lookupKey];\n return prev;\n }\n\n if (typeof currSchema === 'object') {\n if (\n Object.keys(currSchema).filter(\n (k) => k !== 'targetNSAlias' && k !== 'targetNamespace',\n ).length\n ) {\n prev[lookupKey] =\n isArrayExpected && obj[lookupKey] && Array.isArray(obj[lookupKey])\n ? obj[lookupKey].map(reorderKeys(currSchema))\n : reorderKeys(currSchema)(obj[lookupKey]);\n return prev;\n }\n\n prev[lookupKey] = obj[lookupKey];\n return prev;\n }\n\n return prev;\n }, {});\n };\n}\n","import { types } from './types';\nexport { prepareSerializer } from './serializer';\n\ntype T = typeof types;\n\ntype WithOutput = {\n [K in keyof T]: T[K]['output'] extends null ? never : K;\n}[keyof T];\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\nexport const deserializer: { [K in WithOutput]: T[K]['output'] } & Record<\n string,\n any\n> = Object.entries(types).reduce<any>((prev, [key, { output }]) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (output) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n prev[key] = output;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return prev;\n}, {});\n","import type { Instrumentor } from './';\nimport d from '../debug';\n\nexport function withLog<Input, Output>(\n namespace: string,\n): Instrumentor<Input, Output> {\n const debug = d(namespace);\n\n return (fn) => (values, options) => {\n if (values) {\n debug('Called with input %o', values);\n } else {\n debug('Called');\n }\n\n return fn(values, options).then(\n (res) => {\n debug('Succeded');\n return res;\n },\n (err) => {\n debug('Failed');\n return Promise.reject(\n err instanceof Error\n ? err\n : new Error('Unknown error', { cause: err }),\n );\n },\n );\n };\n}\n","// import withLog from './withLog';\nimport type { SoapOptions } from '../../soap';\nimport { withLog } from './withLog';\nimport { pipe } from 'remeda';\n\ntype SoapQuery<Input, Output> = (\n input?: Input,\n options?: SoapOptions,\n) => Promise<Output>;\n\nexport type Instrumentor<Input, Output> = (\n fn: SoapQuery<Input, Output>,\n) => SoapQuery<Input, Output>;\n\nexport function instrument<Input, Output>({\n service,\n query,\n}: {\n service: string;\n query: string;\n}) {\n return (fn: SoapQuery<Input, Output>) =>\n pipe(fn, withLog<Input, Output>(`${service}:${query}`));\n}\n","import type { B2B_Error, Reply, ReplyStatus } from '../Common/types';\n\n/**\n * Represents an error response received from NM B2B\n */\nexport class NMB2BError extends Error {\n /**\n * UTC time at which the request was received at NM.\n *\n * Always set when an XML reply is returned, regardless of the possible exceptions that occurred within the request processing.\n */\n declare requestReceptionTime?: Date;\n\n /**\n * Identification of the request. This id is not unique across time: the request is uniquely identified via two attributes: `requestReceptionTime` and `requestId`.\n *\n * Always set when an XML reply is returned, regardless of the possible exceptions that occurred within the request processing.\n */\n declare requestId?: string;\n\n /**\n * UTC time at which NM has sent the reply.\n *\n * Always set when an XML reply is returned, regardless of the possible exceptions that occurred within the request processing.\n */\n declare sendTime?: Date;\n\n /**\n * Status code explaining the error.\n */\n declare status: Exclude<ReplyStatus, 'OK'>;\n\n /**\n * Contains the input validation errors, if any.\n * Set to null if the request successfully passed input validations (i.e. status is not set to `INVALID_INPUT`).\n */\n declare inputValidationErrors?: Array<B2B_Error>;\n\n /**\n * Contains the output validation errors, if any.\n * Set to null if the request successfully passed output validations (i.e. status is not set to `INVALID_OUTPUT`).\n */\n declare outputValidationErrors?: Array<B2B_Error>;\n\n /**\n * Warnings, if any\n */\n declare warnings?: Array<B2B_Error>;\n\n /**\n * Describes an error caused by a SLA violation.\n *\n */\n declare slaError?: B2B_Error;\n\n declare reason?: string;\n\n constructor({\n reply,\n }: {\n reply: Reply & { status: Exclude<ReplyStatus, 'OK'> };\n }) {\n super();\n\n if (reply.requestId) {\n this.requestId = reply.requestId;\n }\n\n if (reply.requestReceptionTime) {\n this.requestReceptionTime = reply.requestReceptionTime;\n }\n\n if (reply.sendTime) {\n this.sendTime = reply.sendTime;\n }\n\n if (reply.inputValidationErrors) {\n this.inputValidationErrors = reply.inputValidationErrors;\n }\n\n if (reply.warnings) {\n this.warnings = reply.warnings;\n }\n\n if (reply.slaError) {\n this.slaError = reply.slaError;\n }\n\n if (reply.reason) {\n this.reason = reply.reason;\n }\n this.status = reply.status;\n this.message = this.status;\n\n if (this.reason) {\n this.message = `${this.message}: ${this.reason}`;\n }\n }\n}\n","import type { Reply, ReplyStatus, Request } from '../Common/types';\nimport { NMB2BError } from './NMB2BError';\n\nexport function injectSendTime<\n T extends Record<string, any> | null | undefined,\n>(values: T): Request {\n const sendTime = new Date();\n\n if (!values || typeof values !== 'object') {\n return { sendTime };\n }\n\n return { sendTime, ...values };\n}\n\ntype Cb = (...args: any[]) => void;\n\nexport function responseStatusHandler(resolve: Cb, reject: Cb) {\n return (err: unknown, reply: Reply) => {\n if (err) {\n reject(err);\n return;\n }\n\n if (reply.status === 'OK') {\n resolve(reply);\n return;\n } else {\n const err = new NMB2BError({\n reply: reply as Reply & { status: Exclude<ReplyStatus, 'OK'> },\n });\n reject(err);\n return;\n }\n };\n}\n","import type { SoapOptions } from '../soap';\nimport { instrument } from '../utils/instrumentation';\nimport { injectSendTime, responseStatusHandler } from '../utils/internals';\nimport { prepareSerializer } from '../utils/transformers';\nimport type { AirspaceClient } from './';\n\nimport type {\n CompleteAIXMDatasetReply,\n CompleteAIXMDatasetRequest,\n} from './types';\n\nexport type { CompleteAIXMDatasetReply, CompleteAIXMDatasetRequest } from './types';\n\ntype Values = CompleteAIXMDatasetRequest;\ntype Result = CompleteAIXMDatasetReply;\n\nexport type Resolver = (\n values: CompleteAIXMDatasetRequest,\n options?: SoapOptions,\n) => Promise<CompleteAIXMDatasetReply>;\n\nexport default function prepareQueryCompleteAIXMDatasets(\n client: AirspaceClient,\n): Resolver {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const schema =\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n client.describe().AirspaceStructureService.AirspaceStructurePort\n .queryCompleteAIXMDatasets.input;\n const serializer = prepareSerializer(schema);\n\n return instrument<Values, Result>({\n service: 'Airspace',\n query: 'queryCompleteAIXMDatasets',\n })(\n (values, options) =>\n new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n client.queryCompleteAIXMDatasets(\n serializer(injectSendTime(values)),\n options,\n responseStatusHandler(resolve, reject),\n );\n }),\n );\n}\n","import type { SoapOptions } from '../soap';\nimport { instrument } from '../utils/instrumentation';\nimport { injectSendTime, responseStatusHandler } from '../utils/internals';\nimport { prepareSerializer } from '../utils/transformers';\nimport type { AirspaceClient } from './';\n\nimport type { AUPRetrievalReply, AUPRetrievalRequest } from './types';\nexport type { AUPRetrievalReply, AUPRetrievalRequest };\n\ntype Values = AUPRetrievalRequest;\ntype Result = AUPRetrievalReply;\n\nexport type Resolver = (\n values?: Values,\n options?: SoapOptions,\n) => Promise<Result>;\n\nexport default function prepareRetrieveAUP(client: AirspaceClient): Resolver {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const schema =\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n client.describe().AirspaceAvailabilityService.AirspaceAvailabilityPort\n .retrieveAUP.input;\n const serializer = prepareSerializer(schema);\n\n return instrument<Values, Result>({\n service: 'Airspace',\n query: 'retrieveAUP',\n })(\n (values, options) =>\n new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n client.retrieveAUP(\n serializer(injectSendTime(values)),\n options,\n responseStatusHandler(resolve, reject),\n );\n }),\n );\n}\n","import type { SoapOptions } from '../soap';\nimport { instrument } from '../utils/instrumentation';\nimport { injectSendTime, responseStatusHandler } from '../utils/internals';\nimport { prepareSerializer } from '../utils/transformers';\nimport type { AirspaceClient } from './';\nimport type { AUPChainRetrievalReply, AUPChainRetrievalRequest } from './types';\nexport type { AUPChainRetrievalReply, AUPChainRetrievalRequest };\n\ntype Values = AUPChainRetrievalRequest;\ntype Result = AUPChainRetrievalReply;\n\nexport type Resolver = (\n values?: Values,\n options?: SoapOptions,\n) => Promise<Result>;\n\nexport default function prepareRetrieveAUPChain(\n client: AirspaceClient,\n): Resolver {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const schema =\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n client.describe().AirspaceAvailabilityService.AirspaceAvailabilityPort\n .retrieveAUPChain.input;\n const serializer = prepareSerializer(schema);\n\n return instrument<Values, Result>({\n service: 'Airspace',\n query: 'retrieveAUPChain',\n })(\n (values, options) =>\n new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n client.retrieveAUPChain(\n serializer(injectSendTime(values)),\n options,\n responseStatusHandler(resolve, reject),\n );\n }),\n );\n}\n","import type { SoapOptions } from '../soap';\nimport { instrument } from '../utils/instrumentation';\nimport { injectSendTime, responseStatusHandler } from '../utils/internals';\nimport { prepareSerializer } from '../utils/transformers';\nimport type { AirspaceClient } from './';\nimport type {\n EAUPChainRetrievalReply,\n EAUPChainRetrievalRequest,\n} from './types';\n\nexport type { EAUPChainRetrievalReply, EAUPChainRetrievalRequest };\n\ntype Values = EAUPChainRetrievalRequest;\ntype Result = EAUPChainRetrievalReply;\n\nexport type Resolver = (\n values?: Values,\n options?: SoapOptions,\n) => Promise<Result>;\n\nexport default function prepareRetrieveEAUPChain(\n client: AirspaceClient,\n): Resolver {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const schema =\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n client.describe().AirspaceAvailabilityService.AirspaceAvailabilityPort\n .retrieveEAUPChain.input;\n const serializer = prepareSerializer(schema);\n\n return instrument<Values, Result>({\n service: 'Airspace',\n query: 'retrieveEAUPChain',\n })(\n (values, options) =>\n new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n client.retrieveEAUPChain(\n serializer(injectSendTime(values)),\n options,\n responseStatusHandler(resolve, reject),\n );\n }),\n );\n}\n","import { type Client as SoapClient, createClient } from 'soap';\nimport type { Config } from '../config';\nimport { getWSDLPath } from '../constants';\nimport { prepareSecurity } from '../security';\nimport { deserializer as customDeserializer } from '../utils/transformers';\n\nconst getWSDL = ({ flavour, XSD_PATH }: Pick<Config, 'flavour' | 'XSD_PATH'>) =>\n getWSDLPath({ service: 'FlightServices', flavour, XSD_PATH });\n\nexport type FlightClient = SoapClient;\n\nfunction createFlightServices(config: Config): Promise<FlightClient> {\n const WSDL = getWSDL(config);\n const security = prepareSecurity(config);\n return new Promise((resolve, reject) => {\n try {\n createClient(WSDL, { customDeserializer }, (err, client) => {\n if (err) {\n reject(\n err instanceof Error\n ? err\n : new Error('Unknown error', { cause: err }),\n );\n return;\n }\n client.setSecurity(security);\n\n // console.log(util.inspect(client.describe().FlightManagementService.FlightManagementPort.queryFlightPlans, { depth: 3 }));\n // console.log(\n // client.wsdl.definitions.schemas['eurocontrol/cfmu/b2b/CommonServices']\n // .complexTypes['Reply'].children[0].children,\n // );\n resolve(client);\n });\n } catch (err) {\n // TODO: Implement a proper debug log message output\n console.log(err);\n reject(\n err instanceof Error ? err : new Error('Unknown error', { cause: err }),\n );\n return;\n }\n });\n}\n\nimport type { Resolver as RetrieveFlight } from './retrieveFlight';\nimport retrieveFlight from './retrieveFlight';\n\nimport type { Resolver as QueryFlightsByAirspace } from './queryFlightsByAirspace';\nimport queryFlightsByAirspace from './queryFlightsByAirspace';\n\nimport type { Resolver as QueryFlightPlans } from './queryFlightPlans';\nimport queryFlightPlans from './queryFlightPlans';\n\nimport type { Resolver as QueryFlightsByTrafficVolume } from './queryFlightsByTrafficVolume';\nimport queryFlightsByTrafficVolume from './queryFlightsByTrafficVolume';\n\nimport type { Resolver as QueryFlightsByMeasure } from './queryFlightsByMeasure';\nimport queryFlightsByMeasure from './queryFlightsByMeasure';\n\nimport type { Resolver as QueryFlightsByAerodrome } from './queryFlightsByAerodrome';\nimport queryFlightsByAerodrome from './queryFlightsByAerodrome';\n\nimport type { Resolver as QueryFlightsByAerodromeSet } from './queryFlightsByAerodromeSet';\nimport queryFlightsByAerodromeSet from './queryFlightsByAerodromeSet';\n\nimport type { BaseServiceInterface } from '../Common/ServiceInterface';\nimport type { Resolver as QueryFlightsByAircraftOperator } from './queryFlightsByAircraftOperator';\nimport queryFlightsByAircraftOperator from './queryFlightsByAircraftOperator';\n\nexport interface FlightService extends BaseServiceInterface {\n retrieveFlight: RetrieveFlight;\n queryFlightsByAirspace: QueryFlightsByAirspace;\n queryFlightPlans: QueryFlightPlans;\n queryFlightsByTrafficVolume: QueryFlightsByTrafficVolume;\n queryFlightsByMeasure: QueryFlightsByMeasure;\n queryFlightsByAerodrome: QueryFlightsByAerodrome;\n queryFlightsByAerodromeSet: QueryFlightsByAerodromeSet;\n queryFlightsByAircraftOperator: QueryFlightsByAircraftOperator;\n}\n\nexport function getFlightClient(config: Config): Promise<FlightService> {\n return createFlightServices(config).then((client) => ({\n __soapClient: client,\n config,\n retrieveFlight: retrieveFlight(client),\n queryFlightsByAirspace: queryFlightsByAirspace(client),\n queryFlightPlans: queryFlightPlans(client),\n queryFlightsByTrafficVolume: queryFlightsByTrafficVolume(client),\n queryFlightsByMeasure: queryFlightsByMeasure(client),\n queryFlightsByAerodrome: queryFlightsByAerodrome(client),\n queryFlightsByAerodromeSet: queryFlightsByAerodromeSet(client),\n queryFlightsByAircraftOperator: queryFlightsByAircraftOperator(client),\n }));\n}\n","import type { FlightClient } from './';\nimport { injectSendTime, responseStatusHandler } from '../utils/internals';\nimport type { SoapOptions } from '../soap';\nimport { prepareSerializer } from '../utils/transformers';\nimport { instrument } from '../utils/instrumentation';\n\nimport type { FlightRetrievalRequest, FlightRetrievalReply } from './types';\nexport type { FlightRetrievalRequest, FlightRetrievalReply } from './types';\n\ntype Values = FlightRetrievalRequest;\ntype Result = FlightRetrievalReply;\n\nexport type Resolver = (\n values?: Values,\n options?: SoapOptions,\n) => Promise<Result>;\n\nexport default function prepareRetrieveFlight(client: FlightClient): Resolver {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const schema =\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n client.describe().FlightManagementService.FlightManagementPort\n .retrieveFlight.input;\n const serializer = prepareSerializer(schema);\n\n return instrument<Values, Result>({\n service: 'Flight',\n query: 'retrieveFlight',\n })(\n (values, options) =>\n new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n client.retrieveFlight(\n serializer(injectSendTime(values)),\n options,\n responseStatusHandler(resolve, reject),\n );\n }),\n );\n}\n","import type { FlightClient } from './';\nimport { injectSendTime, responseStatusHandler } from '../utils/internals';\nimport type { SoapOptions } from '../soap';\nimport { prepareSerializer } from '../utils/transformers';\nimport { instrument } from '../utils/instrumentation';\n\nimport type {\n FlightListByAirspaceRequest,\n FlightListByAirspaceReply,\n} from './types';\n\nexport type {\n FlightListByAirspaceRequest,\n FlightListByAirspaceReply,\n} from './types';\n\ntype Values = FlightListByAirspaceRequest;\ntype Result = FlightListByAirspaceReply;\n\nexport type Resolver = (\n values?: Values,\n options?: SoapOptions,\n) => Promise<Result>;\n\nexport default function prepareQueryFlightsByAirspace(\n client: FlightClient,\n): Resolver {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const schema =\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n client.describe().FlightManagementService.FlightManagementPort\n .queryFlightsByAirspace.input;\n const serializer = prepareSerializer(schema);\n\n return instrument<Values, Result>({\n service: 'Flight',\n query: 'queryFlightsByAirspace',\n })(\n (values, options) =>\n new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n client.queryFlightsByAirspace(\n serializer(injectSendTime(values)),\n options,\n responseStatusHandler(resolve, reject),\n );\n }),\n );\n}\n","import type { FlightClient } from './';\nimport { injectSendTime, responseStatusHandler } from '../utils/internals';\nimport type { SoapOptions } from '../soap';\nimport { prepareSerializer } from '../utils/transformers';\nimport { instrument } from '../utils/instrumentation';\nimport type { FlightPlanListRequest, FlightPlanListReply } from './types';\nexport type { FlightPlanListRequest, FlightPlanListReply } from './types';\n\ntype Values = FlightPlanListRequest;\ntype Result = FlightPlanListReply;\n\nexport type Resolver = (\n values: Values,\n options?: SoapOptions,\n) => Promise<Result>;\n\nexport default function prepareQueryFlightPlans(\n client: FlightClient,\n): Resolver {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const schema =\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n client.describe().FlightManagementService.FlightManagementPort\n .queryFlightPlans.input;\n const serializer = prepareSerializer(schema);\n\n return instrument<Values, Result>({\n service: 'Flight',\n query: 'queryFlightPlans',\n })(\n (values, options) =>\n new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n client.queryFlightPlans(\n serializer(injectSendTime(values)),\n options,\n responseStatusHandler(resolve, reject),\n );\n }),\n );\n}\n","import type { FlightClient } from './';\nimport { injectSendTime, responseStatusHandler } from '../utils/internals';\nimport type { SoapOptions } from '..