diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d555ce..bd56fe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Master +- Remove moment.js, move to `date-fns` - Update dependencies - Fix missing pkg/config export - Implement `Flight.queryFlightsByAircraftOperator` diff --git a/package.json b/package.json index 35c286d..de4f43a 100644 --- a/package.json +++ b/package.json @@ -107,12 +107,13 @@ "vitest": "^1.2.2" }, "dependencies": { + "@date-fns/utc": "^1.1.1", "@types/sax": "^1.2.7", "axios": "^1.6.7", + "date-fns": "^3.3.1", "debug": "^4.3.4", "invariant": "^2.2.4", "mkdirp": "^3.0.1", - "moment": "^2.30.1", "proper-lockfile": "^4.1.2", "ramda": "~0.28.0", "soap": "^1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f93f71..59e22cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,12 +5,18 @@ settings: excludeLinksFromLockfile: false dependencies: + '@date-fns/utc': + specifier: ^1.1.1 + version: 1.1.1 '@types/sax': specifier: ^1.2.7 version: 1.2.7 axios: specifier: ^1.6.7 version: 1.6.7(debug@4.3.4) + date-fns: + specifier: ^3.3.1 + version: 3.3.1 debug: specifier: ^4.3.4 version: 4.3.4 @@ -20,9 +26,6 @@ dependencies: mkdirp: specifier: ^3.0.1 version: 3.0.1 - moment: - specifier: ^2.30.1 - version: 2.30.1 proper-lockfile: specifier: ^4.1.2 version: 4.1.2 @@ -124,6 +127,10 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true + /@date-fns/utc@1.1.1: + resolution: {integrity: sha512-xHqw5SkB6z2OpouO/6BqLSL1nEI2jLC6WIXT01TNFfffU0Os8U/Gk2yxoKB/qbY44tfnGbqUF+nkST5QqLJpDA==} + dev: false + /@esbuild/aix-ppc64@0.19.11: resolution: {integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==} engines: {node: '>=12'} @@ -859,6 +866,10 @@ packages: which: 2.0.2 dev: true + /date-fns@3.3.1: + resolution: {integrity: sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==} + dev: false + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -1487,10 +1498,6 @@ packages: ufo: 1.3.2 dev: true - /moment@2.30.1: - resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} - dev: false - /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} diff --git a/src/Airspace/queryCompleteAIXMDatasets.test.ts b/src/Airspace/queryCompleteAIXMDatasets.test.ts index b7202d0..43502c9 100644 --- a/src/Airspace/queryCompleteAIXMDatasets.test.ts +++ b/src/Airspace/queryCompleteAIXMDatasets.test.ts @@ -1,7 +1,7 @@ import b2bOptions from '../../tests/options'; import { shouldUseRealB2BConnection } from '../../tests/utils'; import { makeAirspaceClient } from '..'; -import moment from 'moment'; +import { sub } from 'date-fns'; import { describe, test, expect } from 'vitest'; describe('queryCompleteAIXMDatasets', async () => { @@ -11,8 +11,8 @@ describe('queryCompleteAIXMDatasets', async () => { const res = await Airspace.queryCompleteAIXMDatasets({ queryCriteria: { publicationPeriod: { - wef: moment.utc().subtract(28, 'days').toDate(), - unt: moment.utc().toDate(), + wef: sub(new Date(), { days: 28 }), + unt: new Date(), }, }, }); diff --git a/src/Airspace/retrieveAUP.test.ts b/src/Airspace/retrieveAUP.test.ts index e9e2634..261a0df 100644 --- a/src/Airspace/retrieveAUP.test.ts +++ b/src/Airspace/retrieveAUP.test.ts @@ -1,5 +1,5 @@ import { makeAirspaceClient } from '..'; -import moment from 'moment'; +import { sub } from 'date-fns'; import b2bOptions from '../../tests/options'; import { AUPSummary } from './types'; import { shouldUseRealB2BConnection } from '../../tests/utils'; @@ -13,7 +13,7 @@ describe('retrieveAUP', async () => { // Find some AUP id const res = await Airspace.retrieveAUPChain({ amcIds: ['LFFAZAMC'], - chainDate: moment.utc().subtract(1, 'day').toDate(), + chainDate: sub(new Date(), { days: 1 }), }); if (res.data) { diff --git a/src/Airspace/retrieveAUPChain.test.ts b/src/Airspace/retrieveAUPChain.test.ts index cb7be2a..79635f1 100644 --- a/src/Airspace/retrieveAUPChain.test.ts +++ b/src/Airspace/retrieveAUPChain.test.ts @@ -1,4 +1,3 @@ -import moment from 'moment'; import { describe, expect, test } from 'vitest'; import { makeAirspaceClient } from '..'; import b2bOptions from '../../tests/options'; @@ -10,7 +9,7 @@ describe('retrieveAUPChain', async () => { test.runIf(shouldUseRealB2BConnection)('AUP Retrieval', async () => { const res = await Airspace.retrieveAUPChain({ amcIds: ['LFFAZAMC'], - chainDate: moment.utc().toDate(), + chainDate: new Date(), }); if (res.data === null) { diff --git a/src/Airspace/retrieveEAUPChain.test.ts b/src/Airspace/retrieveEAUPChain.test.ts index 113034d..7df5824 100644 --- a/src/Airspace/retrieveEAUPChain.test.ts +++ b/src/Airspace/retrieveEAUPChain.test.ts @@ -1,4 +1,3 @@ -import moment from 'moment'; import { describe, expect, test } from 'vitest'; import { makeAirspaceClient } from '..'; import b2bOptions from '../../tests/options'; @@ -14,7 +13,7 @@ describe('retrieveEAUPChain', async () => { 'EAUP Retrieval', async () => { const res = await Airspace.retrieveEAUPChain({ - chainDate: moment.utc().toDate(), + chainDate: new Date(), }); if (res.data === null) { diff --git a/src/Common/types.ts b/src/Common/types.ts index f799fcb..935d94c 100644 --- a/src/Common/types.ts +++ b/src/Common/types.ts @@ -45,6 +45,7 @@ export interface NMMap { export type DurationHourMinute = number; export type DurationMinute = number; export type DurationHourMinuteSecond = number; +export type Duration = number; export interface DateYearMonthDayPeriod { wef?: DateYearMonthDay; diff --git a/src/Flight/queryFlightPlans.test.ts b/src/Flight/queryFlightPlans.test.ts index 67b64d3..4e28214 100644 --- a/src/Flight/queryFlightPlans.test.ts +++ b/src/Flight/queryFlightPlans.test.ts @@ -1,6 +1,6 @@ import { inspect } from 'util'; import { NMB2BError, makeFlightClient } from '..'; -import moment from 'moment'; +import { sub, add } from 'date-fns'; import b2bOptions from '../../tests/options'; import { FlightOrFlightPlan as B2BFlight } from './types'; import { shouldUseRealB2BConnection } from '../../tests/utils'; @@ -12,14 +12,18 @@ describe('queryFlightPlans', async () => { let knownFlight: B2BFlight | undefined; beforeAll(async () => { + if (!shouldUseRealB2BConnection) { + return; + } + const res = await Flight.queryFlightsByAirspace({ dataset: { type: 'OPERATIONAL' }, includeProposalFlights: false, includeForecastFlights: false, trafficType: 'LOAD', trafficWindow: { - wef: moment.utc().subtract(6, 'hours').toDate(), - unt: moment.utc().add(6, 'hours').toDate(), + wef: sub(new Date(), { hours: 6 }), + unt: add(new Date(), { hours: 6 }), }, airspace: 'LFEERMS', }); @@ -67,14 +71,12 @@ describe('queryFlightPlans', async () => { airFiled: false, nonICAOAerodromeOfDestination: false, estimatedOffBlockTime: { - wef: moment - .utc(knownFlight.flight.flightId.keys?.estimatedOffBlockTime!) - .subtract(30, 'minutes') - .toDate(), - unt: moment - .utc(knownFlight.flight.flightId.keys?.estimatedOffBlockTime!) - .add(30, 'minutes') - .toDate(), + wef: sub(knownFlight.flight.flightId.keys?.estimatedOffBlockTime!, { + minutes: 30, + }), + unt: add(knownFlight.flight.flightId.keys?.estimatedOffBlockTime!, { + minutes: 30, + }), }, }); diff --git a/src/Flight/queryFlightsByAerodrome.test.ts b/src/Flight/queryFlightsByAerodrome.test.ts index a4223bc..80ea8b5 100644 --- a/src/Flight/queryFlightsByAerodrome.test.ts +++ b/src/Flight/queryFlightsByAerodrome.test.ts @@ -1,5 +1,5 @@ +import { add, sub } from 'date-fns'; import { makeFlightClient } from '..'; -import moment from 'moment'; import b2bOptions from '../../tests/options'; import { shouldUseRealB2BConnection } from '../../tests/utils'; import { describe, test, expect } from 'vitest'; @@ -9,8 +9,8 @@ describe('queryFlightsByAirspace', async () => { test.runIf(shouldUseRealB2BConnection)('query in LFPG', async () => { const trafficWindow = { - wef: moment.utc().subtract(10, 'minutes').toDate(), - unt: moment.utc().add(10, 'minutes').toDate(), + wef: sub(new Date(), { minutes: 10 }), + unt: add(new Date(), { minutes: 10 }), }; const res = await Flight.queryFlightsByAerodrome({ diff --git a/src/Flight/queryFlightsByAerodromeSet.test.ts b/src/Flight/queryFlightsByAerodromeSet.test.ts index 8266037..edd8edc 100644 --- a/src/Flight/queryFlightsByAerodromeSet.test.ts +++ b/src/Flight/queryFlightsByAerodromeSet.test.ts @@ -1,5 +1,5 @@ import { makeFlightClient } from '..'; -import moment from 'moment'; +import { add, sub } from 'date-fns'; import b2bOptions from '../../tests/options'; import { shouldUseRealB2BConnection } from '../../tests/utils'; import { describe, test, expect } from 'vitest'; @@ -9,8 +9,8 @@ describe('queryFlightsByAirspace', async () => { test.runIf(shouldUseRealB2BConnection)('query in LFPG+', async () => { const trafficWindow = { - wef: moment.utc().subtract(10, 'minutes').toDate(), - unt: moment.utc().add(10, 'minutes').toDate(), + wef: sub(new Date(), { minutes: 10 }), + unt: add(new Date(), { minutes: 10 }), }; const res = await Flight.queryFlightsByAerodromeSet({ diff --git a/src/Flight/queryFlightsByAircraftOperator.test.ts b/src/Flight/queryFlightsByAircraftOperator.test.ts index 985bd94..c867cd3 100644 --- a/src/Flight/queryFlightsByAircraftOperator.test.ts +++ b/src/Flight/queryFlightsByAircraftOperator.test.ts @@ -1,5 +1,5 @@ import { makeFlightClient } from '..'; -import moment from 'moment'; +import { add, sub } from 'date-fns'; import b2bOptions from '../../tests/options'; import { shouldUseRealB2BConnection } from '../../tests/utils'; import { describe, test, expect } from 'vitest'; @@ -7,55 +7,58 @@ import { describe, test, expect } from 'vitest'; describe('queryFlightsByAircraftOperator', async () => { const Flight = await makeFlightClient(b2bOptions); - test.runIf(shouldUseRealB2BConnection)('query Aircraft Operators', async () => { - const trafficWindow = { - wef: moment.utc().subtract(10, 'minutes').toDate(), - unt: moment.utc().add(10, 'minutes').toDate(), - }; + test.runIf(shouldUseRealB2BConnection)( + 'query Aircraft Operators', + async () => { + const trafficWindow = { + wef: sub(new Date(), { minutes: 10 }), + unt: add(new Date(), { minutes: 10 }), + }; - const res = await Flight.queryFlightsByAircraftOperator({ - dataset: { type: 'OPERATIONAL' }, - includeProposalFlights: false, - includeForecastFlights: false, - trafficType: 'LOAD', - trafficWindow, - aircraftOperators: ['AFR', 'RYR', 'UAE'], - calculationType: 'OCCUPANCY', // Optional, default: 'ENTRY', - }); + const res = await Flight.queryFlightsByAircraftOperator({ + dataset: { type: 'OPERATIONAL' }, + includeProposalFlights: false, + includeForecastFlights: false, + trafficType: 'LOAD', + trafficWindow, + aircraftOperators: ['AFR', 'RYR', 'UAE'], + calculationType: 'OCCUPANCY', // Optional, default: 'ENTRY', + }); - /** - * Here, we ensure the returned traffic window matches the supplied - * traffic window, with a 60s precision. - */ - expect( - Math.abs( - res.data.effectiveTrafficWindow.wef.getTime() - - trafficWindow.wef.getTime(), - ), - ).toBeLessThan(60 * 1000); + /** + * Here, we ensure the returned traffic window matches the supplied + * traffic window, with a 60s precision. + */ + expect( + Math.abs( + res.data.effectiveTrafficWindow.wef.getTime() - + trafficWindow.wef.getTime(), + ), + ).toBeLessThan(60 * 1000); - expect( - Math.abs( - res.data.effectiveTrafficWindow.unt.getTime() - - trafficWindow.unt.getTime(), - ), - ).toBeLessThan(60 * 1000); + expect( + Math.abs( + res.data.effectiveTrafficWindow.unt.getTime() - + trafficWindow.unt.getTime(), + ), + ).toBeLessThan(60 * 1000); - expect(res.data?.flights).toEqual(expect.any(Array)); - for (const flight of res.data?.flights) { - expect(flight).toMatchObject({ - flight: { - flightId: { - id: expect.any(String), - keys: { - aircraftId: expect.any(String), - aerodromeOfDeparture: expect.stringMatching(/^[A-Z]{4}$/), - aerodromeOfDestination: expect.stringMatching(/^[A-Z]{4}$/), - estimatedOffBlockTime: expect.any(Date), + expect(res.data?.flights).toEqual(expect.any(Array)); + for (const flight of res.data?.flights) { + expect(flight).toMatchObject({ + flight: { + flightId: { + id: expect.any(String), + keys: { + aircraftId: expect.any(String), + aerodromeOfDeparture: expect.stringMatching(/^[A-Z]{4}$/), + aerodromeOfDestination: expect.stringMatching(/^[A-Z]{4}$/), + estimatedOffBlockTime: expect.any(Date), + }, }, }, - }, - }); - } - }); + }); + } + }, + ); }); diff --git a/src/Flight/queryFlightsByAirspace.test.ts b/src/Flight/queryFlightsByAirspace.test.ts index f1666a5..2a00a67 100644 --- a/src/Flight/queryFlightsByAirspace.test.ts +++ b/src/Flight/queryFlightsByAirspace.test.ts @@ -1,5 +1,5 @@ import { makeFlightClient } from '..'; -import moment from 'moment'; +import { add, sub } from 'date-fns'; import b2bOptions from '../../tests/options'; import { shouldUseRealB2BConnection } from '../../tests/utils'; import { describe, test, expect } from 'vitest'; @@ -9,8 +9,8 @@ describe('queryFlightsByAirspace', async () => { test.runIf(shouldUseRealB2BConnection)('query in LFEERMS', async () => { const trafficWindow = { - wef: moment.utc().subtract(10, 'minutes').toDate(), - unt: moment.utc().add(10, 'minutes').toDate(), + wef: sub(new Date(), { minutes: 10 }), + unt: add(new Date(), { minutes: 10 }), }; const res = await Flight.queryFlightsByAirspace({ diff --git a/src/Flight/queryFlightsByMeasure.test.ts b/src/Flight/queryFlightsByMeasure.test.ts index 5a79d3e..39a642e 100644 --- a/src/Flight/queryFlightsByMeasure.test.ts +++ b/src/Flight/queryFlightsByMeasure.test.ts @@ -1,10 +1,10 @@ import { inspect } from 'util'; import { NMB2BError, makeFlightClient, makeFlowClient } from '..'; -import moment from 'moment'; import b2bOptions from '../../tests/options'; import { Regulation } from '../Flow/types'; import { beforeAll, describe, expect, test } from 'vitest'; import { shouldUseRealB2BConnection } from '../../tests/utils'; +import { sub, add, startOfHour } from 'date-fns'; describe('queryFlightsByMeasure', async () => { let measure: void | Regulation; @@ -15,8 +15,8 @@ describe('queryFlightsByMeasure', async () => { ]); const window = { - wef: moment.utc().subtract(2, 'hour').startOf('hour').toDate(), - unt: moment.utc().add(2, 'hour').startOf('hour').toDate(), + wef: startOfHour(sub(new Date(), { hours: 2 })), + unt: startOfHour(add(new Date(), { hours: 2 })), }; beforeAll(async () => { diff --git a/src/Flight/queryFlightsByTrafficVolume.test.ts b/src/Flight/queryFlightsByTrafficVolume.test.ts index 2dcaaae..5446fc7 100644 --- a/src/Flight/queryFlightsByTrafficVolume.test.ts +++ b/src/Flight/queryFlightsByTrafficVolume.test.ts @@ -1,16 +1,16 @@ import { makeFlightClient } from '..'; -import moment from 'moment'; import b2bOptions from '../../tests/options'; import { shouldUseRealB2BConnection } from '../../tests/utils'; import { describe, expect, test } from 'vitest'; +import { add, sub } from 'date-fns'; describe('queryFlightsByTrafficVolume', async () => { const Flight = await makeFlightClient(b2bOptions); test.runIf(shouldUseRealB2BConnection)('query in LFERMS', async () => { const trafficWindow = { - wef: moment.utc().subtract(2, 'hours').toDate(), - unt: moment.utc().add(2, 'hours').toDate(), + wef: sub(new Date(), { hours: 2 }), + unt: add(new Date(), { hours: 2 }), }; const res = await Flight.queryFlightsByTrafficVolume({ diff --git a/src/Flight/retrieveFlight.test.ts b/src/Flight/retrieveFlight.test.ts index 30735e7..2d2f69a 100644 --- a/src/Flight/retrieveFlight.test.ts +++ b/src/Flight/retrieveFlight.test.ts @@ -1,10 +1,10 @@ import { inspect } from 'util'; import { NMB2BError, makeFlightClient } from '..'; -import moment from 'moment'; import b2bOptions from '../../tests/options'; import type { FlightKeys } from './types'; import { shouldUseRealB2BConnection } from '../../tests/utils'; import { expect, beforeAll, test, describe } from 'vitest'; +import { add, sub } from 'date-fns'; describe('retrieveFlight', async () => { const Flight = await makeFlightClient(b2bOptions); @@ -21,8 +21,8 @@ describe('retrieveFlight', async () => { includeForecastFlights: false, trafficType: 'LOAD', trafficWindow: { - wef: moment.utc().subtract(30, 'minutes').toDate(), - unt: moment.utc().add(30, 'minutes').toDate(), + wef: sub(new Date(), { minutes: 30 }), + unt: add(new Date(), { minutes: 30 }), }, airspace: 'LFEERMS', }); diff --git a/src/Flight/types.ts b/src/Flight/types.ts index ba00d49..b56f6fb 100644 --- a/src/Flight/types.ts +++ b/src/Flight/types.ts @@ -52,7 +52,6 @@ export type ATFMMessageType = | 'SWM' | 'UNK'; -import { Duration } from 'moment'; import { AerodromeICAOId, FlightLevel, @@ -98,6 +97,7 @@ import { FlightLevelM, Cost, SignedDurationHourMinuteSecond, + Duration, } from '../Common/types'; import { @@ -1408,7 +1408,7 @@ export interface FlightListByAerodromeSetReplyData export interface FlightListByAircraftOperatorRequest extends FlightListByLocationRequest { - calculationType?: CountsCalculationType; + calculationType?: CountsCalculationType; } export interface FlightListByAircraftOperatorReply extends Reply { diff --git a/src/Flow/queryRegulations.test.ts b/src/Flow/queryRegulations.test.ts index 18442f6..ddeebc2 100644 --- a/src/Flow/queryRegulations.test.ts +++ b/src/Flow/queryRegulations.test.ts @@ -1,12 +1,11 @@ -import moment from 'moment'; import { inspect } from 'util'; import { describe, expect, test } from 'vitest'; import { NMB2BError, makeFlowClient } from '..'; import b2bOptions from '../../tests/options'; import { shouldUseRealB2BConnection } from '../../tests/utils'; - import type { RegulationListReply } from './queryRegulations'; import { extractReferenceLocation } from '../utils/extractReferenceLocation'; +import { add, startOfHour, sub } from 'date-fns'; describe('queryRegulations', async () => { const Flow = await makeFlowClient(b2bOptions); @@ -16,8 +15,8 @@ describe('queryRegulations', async () => { const res: RegulationListReply = await Flow.queryRegulations({ dataset: { type: 'OPERATIONAL' }, queryPeriod: { - wef: moment.utc().subtract(10, 'hour').startOf('hour').toDate(), - unt: moment.utc().add(10, 'hour').startOf('hour').toDate(), + wef: startOfHour(sub(new Date(), { hours: 10 })), + unt: startOfHour(add(new Date(), { hours: 10 })), }, requestedRegulationFields: { item: [ diff --git a/src/Flow/queryTrafficCountsByAirspace.test.ts b/src/Flow/queryTrafficCountsByAirspace.test.ts index 21a750a..5d6d034 100644 --- a/src/Flow/queryTrafficCountsByAirspace.test.ts +++ b/src/Flow/queryTrafficCountsByAirspace.test.ts @@ -1,9 +1,9 @@ -import moment from 'moment'; import { inspect } from 'util'; import { describe, expect, test } from 'vitest'; import { NMB2BError, makeFlowClient } from '..'; import b2bOptions from '../../tests/options'; import { shouldUseRealB2BConnection } from '../../tests/utils'; +import { add, startOfHour, sub } from 'date-fns'; describe('queryTrafficCountsByAirspace', async () => { const Flow = await makeFlowClient(b2bOptions); @@ -13,8 +13,8 @@ describe('queryTrafficCountsByAirspace', async () => { const res = await Flow.queryTrafficCountsByAirspace({ dataset: { type: 'OPERATIONAL' }, trafficWindow: { - wef: moment.utc().subtract(1, 'hour').startOf('hour').toDate(), - unt: moment.utc().add(1, 'hour').startOf('hour').toDate(), + wef: startOfHour(sub(new Date(), { hours: 1 })), + unt: startOfHour(add(new Date(), { hours: 1 })), }, includeProposalFlights: false, includeForecastFlights: false, diff --git a/src/Flow/queryTrafficCountsByTrafficVolume.test.ts b/src/Flow/queryTrafficCountsByTrafficVolume.test.ts index f1bf50f..a37e43a 100644 --- a/src/Flow/queryTrafficCountsByTrafficVolume.test.ts +++ b/src/Flow/queryTrafficCountsByTrafficVolume.test.ts @@ -1,9 +1,9 @@ -import moment from 'moment'; import { inspect } from 'util'; import { describe, expect, test } from 'vitest'; import { NMB2BError, makeFlowClient } from '..'; import b2bOptions from '../../tests/options'; import { shouldUseRealB2BConnection } from '../../tests/utils'; +import { add, startOfHour, sub } from 'date-fns'; describe('queryTrafficCountsByTrafficVolume', async () => { const Flow = await makeFlowClient(b2bOptions); @@ -13,8 +13,8 @@ describe('queryTrafficCountsByTrafficVolume', async () => { const res = await Flow.queryTrafficCountsByTrafficVolume({ dataset: { type: 'OPERATIONAL' }, trafficWindow: { - wef: moment.utc().subtract(1, 'hour').startOf('hour').toDate(), - unt: moment.utc().add(1, 'hour').startOf('hour').toDate(), + wef: startOfHour(sub(new Date(), { hours: 1 })), + unt: startOfHour(add(new Date(), { hours: 1 })), }, includeProposalFlights: false, includeForecastFlights: false, diff --git a/src/Flow/retrieveCapacityPlan.test.ts b/src/Flow/retrieveCapacityPlan.test.ts index 1531742..f3c86e5 100644 --- a/src/Flow/retrieveCapacityPlan.test.ts +++ b/src/Flow/retrieveCapacityPlan.test.ts @@ -1,6 +1,5 @@ import { inspect } from 'util'; import { NMB2BError, makeFlowClient } from '..'; -import moment from 'moment'; import b2bOptions from '../../tests/options'; import { Result as CapacityPlanRetrievalResult } from './retrieveCapacityPlan'; import { describe, test, expect } from 'vitest'; @@ -13,7 +12,7 @@ describe('retrieveCapacityPlan', async () => { try { const res: CapacityPlanRetrievalResult = await Flow.retrieveCapacityPlan({ dataset: { type: 'OPERATIONAL' }, - day: moment.utc().toDate(), + day: new Date(), trafficVolumes: { item: ['LFERMS', 'LFBBDX'], }, diff --git a/src/Flow/retrieveOTMVPlan.test.ts b/src/Flow/retrieveOTMVPlan.test.ts index 15ed7b5..1d3ac95 100644 --- a/src/Flow/retrieveOTMVPlan.test.ts +++ b/src/Flow/retrieveOTMVPlan.test.ts @@ -1,6 +1,5 @@ import { inspect } from 'util'; import { NMB2BError, makeFlowClient } from '..'; -import moment from 'moment'; import b2bOptions from '../../tests/options'; import { describe, test, expect } from 'vitest'; import { shouldUseRealB2BConnection } from '../../tests/utils'; @@ -12,7 +11,7 @@ describe('retrieveOTMVPlan', async () => { try { const res = await Flow.retrieveOTMVPlan({ dataset: { type: 'OPERATIONAL' }, - day: moment.utc().toDate(), + day: new Date(), otmvsWithDuration: { item: [{ trafficVolume: 'LFERMS' }], }, diff --git a/src/Flow/retrieveRunwayConfigurationPlan.test.ts b/src/Flow/retrieveRunwayConfigurationPlan.test.ts index 098a10e..2809f17 100644 --- a/src/Flow/retrieveRunwayConfigurationPlan.test.ts +++ b/src/Flow/retrieveRunwayConfigurationPlan.test.ts @@ -1,6 +1,5 @@ import { inspect } from 'util'; -import { makeFlowClient, B2BClient, NMB2BError } from '..'; -import moment from 'moment'; +import { makeFlowClient, NMB2BError } from '..'; import b2bOptions from '../../tests/options'; import { RunwayConfigurationPlanRetrievalReply } from './types'; import { expect, test, describe } from 'vitest'; @@ -14,7 +13,7 @@ describe('retrieveRunwayConfigurationPlan', async () => { const res: RunwayConfigurationPlanRetrievalReply = await Flow.retrieveRunwayConfigurationPlan({ dataset: { type: 'OPERATIONAL' }, - day: moment.utc().toDate(), + day: new Date(), aerodrome: 'LFBD', }); diff --git a/src/Flow/retrieveSectorConfigurationPlan.test.ts b/src/Flow/retrieveSectorConfigurationPlan.test.ts index e4c4175..2af37ff 100644 --- a/src/Flow/retrieveSectorConfigurationPlan.test.ts +++ b/src/Flow/retrieveSectorConfigurationPlan.test.ts @@ -1,4 +1,3 @@ -import moment from 'moment'; import { inspect } from 'util'; import { describe, expect, test } from 'vitest'; import { NMB2BError, makeFlowClient } from '..'; @@ -13,7 +12,7 @@ describe('retrieveSectorConfigurationPlan', async () => { try { const res = await Flow.retrieveSectorConfigurationPlan({ dataset: { type: 'OPERATIONAL' }, - day: moment.utc().toDate(), + day: new Date(), airspace: 'LFEECTAN', }); diff --git a/src/Flow/updateCapacityPlan.test.ts b/src/Flow/updateCapacityPlan.test.ts index 6d9661f..98fb565 100644 --- a/src/Flow/updateCapacityPlan.test.ts +++ b/src/Flow/updateCapacityPlan.test.ts @@ -1,10 +1,10 @@ -import moment from 'moment'; import { inspect } from 'util'; import { describe, expect, test } from 'vitest'; import { NMB2BError, makeFlowClient } from '..'; import b2bOptions from '../../tests/options'; import { Result as CapacityPlanRetrievalResult } from './retrieveCapacityPlan'; import { Result as CapacityPlanUpdateResult } from './updateCapacityPlan'; +import { add, startOfDay } from 'date-fns'; describe('updateCapacityPlan', async () => { const Flow = await makeFlowClient(b2bOptions); @@ -14,7 +14,7 @@ describe('updateCapacityPlan', async () => { const plan: CapacityPlanRetrievalResult = await Flow.retrieveCapacityPlan( { dataset: { type: 'OPERATIONAL' }, - day: moment.utc().toDate(), + day: new Date(), trafficVolumes: { item: ['LFERMS'], }, @@ -28,12 +28,12 @@ describe('updateCapacityPlan', async () => { return; } - const hPlus10Min: moment.Moment = moment.utc().add(10, 'minute'); + const hPlus10Min = add(new Date(), { minutes: 10 }); const res: CapacityPlanUpdateResult = await Flow.updateCapacityPlan({ plans: { dataId: plan.data.plans.dataId, dataset: { type: 'OPERATIONAL' }, - day: moment.utc().toDate(), + day: new Date(), tvCapacities: { item: [ { @@ -43,19 +43,15 @@ describe('updateCapacityPlan', async () => { item: [ { applicabilityPeriod: { - wef: moment.utc().startOf('day').toDate(), //.format('YYYY-MM-DD HH:mm'),//.toDate(), - unt: hPlus10Min.toDate(), + wef: startOfDay(new Date()), + unt: hPlus10Min, }, dataSource: 'AIRSPACE', }, { applicabilityPeriod: { - wef: hPlus10Min.toDate(), - unt: moment - .utc() - .add(1, 'day') - .startOf('day') - .toDate(), + wef: hPlus10Min, + unt: startOfDay(add(new Date(), { days: 1 })), }, dataSource: 'TACTICAL', capacity: 2, diff --git a/src/Flow/updateOTMVPlan.test.ts b/src/Flow/updateOTMVPlan.test.ts index df157fd..85003de 100644 --- a/src/Flow/updateOTMVPlan.test.ts +++ b/src/Flow/updateOTMVPlan.test.ts @@ -1,11 +1,10 @@ -import { inspect } from 'util'; -import { makeFlowClient, B2BClient } from '..'; -import moment from 'moment'; +import { makeFlowClient } from '..'; import b2bOptions from '../../tests/options'; import { Result as OTMVPlanUpdateResult } from './updateOTMVPlan'; import { Result as OTMVPlanRetrievalResult } from './retrieveOTMVPlan'; -import { FlowService } from '.'; import { describe, expect, beforeAll, afterAll, test } from 'vitest'; +import { add, startOfDay } from 'date-fns'; +import { UTCDateMini } from '@date-fns/utc'; describe('updateOTMVPlan', async () => { const Flow = await makeFlowClient(b2bOptions); @@ -14,7 +13,7 @@ describe('updateOTMVPlan', async () => { beforeAll(async () => { const res = await Flow.retrieveOTMVPlan({ dataset: { type: 'OPERATIONAL' }, - day: moment.utc().toDate(), + day: new Date(), otmvsWithDuration: { item: [ { @@ -65,13 +64,13 @@ describe('updateOTMVPlan', async () => { return; } - const hPlus10Min: moment.Moment = moment.utc().add(10, 'minute'); + const hPlus10Min = add(new Date(), { minutes: 10 }); const res: OTMVPlanUpdateResult = await Flow.updateOTMVPlan({ plans: { dataId: planBefore.plans.dataId, dataset: { type: 'OPERATIONAL' }, - day: moment.utc().toDate(), + day: new Date(), tvsOTMVs: { item: [ { @@ -85,19 +84,17 @@ describe('updateOTMVPlan', async () => { item: [ { applicabilityPeriod: { - wef: moment.utc().startOf('day').toDate(), //.format('YYYY-MM-DD HH:mm'),//.toDate(), - unt: hPlus10Min.toDate(), + wef: startOfDay(new UTCDateMini()), + unt: hPlus10Min, }, dataSource: 'AIRSPACE', }, { applicabilityPeriod: { - wef: hPlus10Min.toDate(), - unt: moment - .utc() - .add(1, 'day') - .startOf('day') - .toDate(), + wef: hPlus10Min, + unt: add(startOfDay(new UTCDateMini()), { + days: 1, + }), }, dataSource: 'TACTICAL', otmv: { diff --git a/src/utils/injectSendTime.ts b/src/utils/injectSendTime.ts index 6152231..68c7eae 100644 --- a/src/utils/injectSendTime.ts +++ b/src/utils/injectSendTime.ts @@ -1,13 +1,11 @@ -import moment from 'moment'; -import { timeFormatWithSeconds } from './timeFormats'; import { Request } from '../Common/types'; export default function injectSendTime(values: T): Request { - const sendTime = moment.utc().toDate(); + const sendTime = new Date(); if (!values || typeof values !== 'object') { return { sendTime }; } - return { sendTime, ...values}; + return { sendTime, ...values }; } diff --git a/src/utils/timeFormats.ts b/src/utils/timeFormats.ts index 550e510..4a6f5ba 100644 --- a/src/utils/timeFormats.ts +++ b/src/utils/timeFormats.ts @@ -1,3 +1,3 @@ -export const timeFormat = 'YYYY-MM-DD HH:mm'; -export const dateFormat = 'YYYY-MM-DD'; +export const timeFormat = 'yyyy-MM-dd HH:mm'; +export const dateFormat = 'yyyy-MM-dd'; export const timeFormatWithSeconds = timeFormat + ':ss'; diff --git a/src/utils/transformers/serializer.test.ts b/src/utils/transformers/serializer.test.ts index 88462d0..cfc7d4f 100644 --- a/src/utils/transformers/serializer.test.ts +++ b/src/utils/transformers/serializer.test.ts @@ -1,6 +1,6 @@ import { reorderKeys, prepareSerializer } from './serializer'; -import moment from 'moment'; import { describe, test, expect } from 'vitest'; +import { UTCDate } from '@date-fns/utc'; describe('reorderKeys', () => { const testCases = [ @@ -153,7 +153,7 @@ describe('retrieveOTMVPlan', () => { test('should serialize otmvDuration properly', () => { const serialize = prepareSerializer(schema); - const now = new Date(); + const now = new Date('2024-02-07T23:30:00.000Z'); const prepared = serialize({ dataset: { type: 'OPERATIONAL' }, @@ -168,7 +168,7 @@ describe('retrieveOTMVPlan', () => { dataset: { type: 'OPERATIONAL', }, - day: moment.utc(now).format('YYYY-MM-DD'), + day: new UTCDate(now).toISOString().slice(0, 10), otmvsWithDuration: { item: [ { diff --git a/src/utils/transformers/types.test.ts b/src/utils/transformers/types.test.ts index c75fc33..606a1f6 100644 --- a/src/utils/transformers/types.test.ts +++ b/src/utils/transformers/types.test.ts @@ -1,54 +1,68 @@ import { types } from './types'; -import { test, expect } from 'vitest'; +import { test, expect, describe } from 'vitest'; -const serialization = [ - { type: 'DurationMinute', input: 34 * 60, expected: 34 }, - { type: 'DurationHourMinute', input: 34 * 60, expected: '0034' }, - { type: 'DurationHourMinute', input: 34 * 60 + 30, expected: '0034' }, - { - type: 'DurationHourMinuteSecond', - input: 2 * 3600 + 30 * 60 + 45, - expected: '023045', - }, - { - type: 'DateTimeSecond', - input: new Date('2018-07-01T17:55:13-07:00'), - expected: '2018-07-02 00:55:13', - }, -] satisfies Array<{ type: keyof typeof types; expected: any; input: any }>; +describe('serialization', () => { + const serialization = [ + { type: 'DurationMinute', input: 34 * 60, expected: 34 }, + { type: 'DurationHourMinute', input: 34 * 60, expected: '0034' }, + { type: 'DurationHourMinute', input: 34 * 60 + 30, expected: '0034' }, + { + type: 'DurationHourMinuteSecond', + input: 2 * 3600 + 30 * 60 + 45, + expected: '023045', + }, + { + type: 'DateTimeSecond', + input: new Date('2018-07-01T17:55:13-07:00'), + expected: '2018-07-02 00:55:13', + }, + ] satisfies Array<{ type: keyof typeof types; expected: any; input: any }>; -test.each(serialization)( - '$type .input($input) => $expected', - ({ type, expected, input }) => { - expect(types[type].input(input as any)).toEqual(expected); - }, -); + test.each(serialization)( + '$type .input($input) => $expected', + ({ type, expected, input }) => { + expect(types[type].input(input as any)).toEqual(expected); + }, + ); +}); -const deserialization = [ - { type: 'DurationMinute', input: 34, expected: 34 * 60 }, - { type: 'DurationHourMinute', input: '0034', expected: 34 * 60 }, - { - type: 'DurationHourMinute', - input: '0210', - expected: 2 * 60 * 60 + 10 * 60, - }, - { - type: 'DurationHourMinuteSecond', - input: '023045', - expected: 2 * 3600 + 30 * 60 + 45, - }, - { type: 'CountsValue', input: '34', expected: 34 }, - { - type: 'DateTimeSecond', - input: '2018-07-02 00:55:13', - expected: new Date('2018-07-01T17:55:13-07:00'), - }, -] satisfies Array<{ type: keyof typeof types; input: any; expected: any }>; +describe('deserialization', () => { + const deserialization = [ + { type: 'DurationMinute', input: 34, expected: 34 * 60 }, + { type: 'DurationHourMinute', input: '0034', expected: 34 * 60 }, + { + type: 'DurationHourMinute', + input: '0210', + expected: 2 * 60 * 60 + 10 * 60, + }, + { + type: 'DurationHourMinuteSecond', + input: '023045', + expected: 2 * 3600 + 30 * 60 + 45, + }, + { type: 'CountsValue', input: '34', expected: 34 }, + { + type: 'DateTimeSecond', + input: '2018-07-02 00:55:13', + expected: new Date('2018-07-02T00:55:13Z'), + }, + { + type: 'DateTimeSecond', + input: '2018-07-02 00:55', + expected: new Date('2018-07-02T00:55:00Z'), + }, + { + type: 'DateTimeSecond', + input: '2018-07-02', + expected: new Date('2018-07-02T00:00:00Z'), + }, + ] satisfies Array<{ type: keyof typeof types; input: any; expected: any }>; -test.each(deserialization)( - '$type .output($input) => $expected', - ({ type, input, expected }) => { - const deserializer: any = types[type].output; - expect(deserializer(input)).toEqual(expected); - }, -); + test.each(deserialization)( + '$type .output($input) => $expected', + ({ type, input, expected }) => { + const deserializer: any = types[type].output; + expect(deserializer(input)).toEqual(expected); + }, + ); +}); diff --git a/src/utils/transformers/types.ts b/src/utils/transformers/types.ts index 1ba0243..3067774 100644 --- a/src/utils/transformers/types.ts +++ b/src/utils/transformers/types.ts @@ -1,13 +1,38 @@ -import moment from 'moment'; import * as timeFormats from '../timeFormats'; +import { format } from 'date-fns'; +import { UTCDate } from '@date-fns/utc'; const outputBase = { integer: (text: string) => { return parseInt(text, 10); }, + /** + * + * Parse a NMB2B date/datetime. + * + * All datetimes are assumed to be UTC. + * + * Per NM B2B documentation, we only need to support these formats: + * - DateTimeMinute: YYYY-MM-DD hh:mm + * - DateTimeSecond: YYYY-MM-DD hh:mm:ss + * - DateYearMonthDay: YYYY-MM-DD + * + * All dates are + * @param text NM B2B Date string + * @returns Parsed Date instance + */ date: (text: string) => { - const d = moment.utc(text).toDate(); - return d; + let [date, time] = text.split(' '); + + if (!time) { + return new Date(date); + } + + if (time.length === 5) { + time += ':00'; + } + + return new Date(`${date}T${time}Z`); }, }; @@ -65,16 +90,16 @@ export const types = { output: outputBase.integer, }, DateTimeMinute: { - input: (d: Date): string => moment(d).utc().format(timeFormats.timeFormat), + input: (d: Date): string => format(new UTCDate(d), timeFormats.timeFormat), output: outputBase.date, }, DateYearMonthDay: { - input: (d: Date): string => moment(d).utc().format(timeFormats.dateFormat), + input: (d: Date): string => format(new UTCDate(d), timeFormats.dateFormat), output: outputBase.date, }, DateTimeSecond: { input: (d: Date): string => - moment(d).utc().format(timeFormats.timeFormatWithSeconds), + format(new UTCDate(d), timeFormats.timeFormatWithSeconds), output: outputBase.date, }, DistanceNM: { diff --git a/src/utils/xsd/filePath.ts b/src/utils/xsd/filePath.ts index 165bf29..65aa10f 100644 --- a/src/utils/xsd/filePath.ts +++ b/src/utils/xsd/filePath.ts @@ -1,14 +1,10 @@ -import axios, { AxiosError } from 'axios'; -import { fromEnv } from '../../security'; -import dotenv from 'dotenv'; -import path from 'path'; -import moment from 'moment'; +import axios from 'axios'; +import { UTCDateMini } from '@date-fns/utc'; +import { format } from 'date-fns'; import { timeFormatWithSeconds } from '../timeFormats'; import { B2B_VERSION, B2BFlavour } from '../../constants'; import { getEndpoint } from '../../config'; import { Security } from '../../security'; -import d from '../debug'; -const debug = d('wsdl-downloader'); import { createAxiosConfig } from './createAxiosConfig'; const makeQuery = ({ version }: { version: string }) => ` @@ -20,7 +16,7 @@ const makeQuery = ({ version }: { version: string }) => ` - ${moment.utc().format(timeFormatWithSeconds)} + ${format(new UTCDateMini(), timeFormatWithSeconds)} ${version}