@opentripplanner/core-utils 14.0.0-alpha.1 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -4
- package/esm/itinerary.js +74 -32
- package/esm/itinerary.js.map +1 -1
- package/esm/otpSchema.json +7321 -5821
- package/esm/planQuery.graphql +12 -0
- package/esm/profile.js +1 -1
- package/esm/profile.js.map +1 -1
- package/esm/query-gen.js +2 -2
- package/esm/query-gen.js.map +1 -1
- package/esm/query.js +2 -5
- package/esm/query.js.map +1 -1
- package/esm/storage.js +2 -6
- package/esm/storage.js.map +1 -1
- package/esm/time.js +1 -2
- package/esm/time.js.map +1 -1
- package/lib/index.js +50 -35
- package/lib/index.js.map +1 -1
- package/lib/itinerary.d.ts +25 -8
- package/lib/itinerary.d.ts.map +1 -1
- package/lib/itinerary.js +524 -491
- package/lib/itinerary.js.map +1 -1
- package/lib/map.js +40 -39
- package/lib/map.js.map +1 -1
- package/lib/otpSchema.json +7321 -5821
- package/lib/planQuery.graphql +12 -0
- package/lib/profile.js +1 -1
- package/lib/profile.js.map +1 -1
- package/lib/query-gen.js +134 -138
- package/lib/query-gen.js.map +1 -1
- package/lib/query.js +3 -6
- package/lib/query.js.map +1 -1
- package/lib/route.js +230 -248
- package/lib/route.js.map +1 -1
- package/lib/storage.d.ts.map +1 -1
- package/lib/storage.js +22 -28
- package/lib/storage.js.map +1 -1
- package/lib/suspense.js +28 -16
- package/lib/suspense.js.map +1 -1
- package/lib/time.js +36 -49
- package/lib/time.js.map +1 -1
- package/lib/ui.js +33 -36
- package/lib/ui.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/__snapshots__/query-params.ts.snap +201 -201
- package/src/__tests__/__snapshots__/query.js.snap +28 -28
- package/src/__tests__/__snapshots__/route.js.snap +76 -76
- package/src/__tests__/__snapshots__/time.js.snap +2 -2
- package/src/__tests__/itinerary.ts +108 -23
- package/src/__tests__/query.js +1 -1
- package/src/__tests__/route.js +1 -1
- package/src/core-utils.story.tsx +34 -23
- package/src/itinerary.ts +88 -37
- package/src/otpSchema.json +7321 -5821
- package/src/planQuery.graphql +12 -0
- package/src/profile.js +1 -1
- package/src/query-gen.ts +1 -1
- package/src/query.js +2 -5
- package/src/storage.ts +5 -9
- package/tsconfig.tsbuildinfo +1 -1
package/src/__tests__/query.js
CHANGED
package/src/__tests__/route.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
makeRouteComparator
|
|
6
6
|
} from "../route";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
import { otp1Routes, otp2Routes } from "./__mocks__/routes.json";
|
|
9
9
|
|
|
10
10
|
function sortRoutes(...routes) {
|
|
11
11
|
routes.sort(makeRouteComparator());
|
package/src/core-utils.story.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable react/display-name */
|
|
1
2
|
import React, { useState } from "react";
|
|
2
3
|
import styled from "styled-components";
|
|
3
4
|
import {
|
|
@@ -42,29 +43,39 @@ const ColorPair = ({ fg, bg }: { fg: string; bg: string }) => {
|
|
|
42
43
|
);
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
export const RouteColorTester =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
46
|
+
export const RouteColorTester = {
|
|
47
|
+
render: (): JSX.Element => {
|
|
48
|
+
const [fg, setFg] = useState("#333333");
|
|
49
|
+
const [bg, setBg] = useState("#cbeb55");
|
|
50
|
+
return (
|
|
51
|
+
<>
|
|
52
|
+
<ColorPair bg={bg} fg={fg}></ColorPair>
|
|
53
|
+
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
|
54
|
+
<label>
|
|
55
|
+
Foreground Color
|
|
56
|
+
<input
|
|
57
|
+
onChange={e => setFg(e.target.value)}
|
|
58
|
+
type="color"
|
|
59
|
+
value={fg}
|
|
60
|
+
/>
|
|
61
|
+
</label>
|
|
62
|
+
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
|
63
|
+
<label>
|
|
64
|
+
Background Color
|
|
65
|
+
<input
|
|
66
|
+
onChange={e => setBg(e.target.value)}
|
|
67
|
+
type="color"
|
|
68
|
+
value={bg}
|
|
69
|
+
/>
|
|
70
|
+
</label>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
// Disable color contrast checking for the uncorrected color pairs
|
|
75
|
+
parameters: {
|
|
76
|
+
a11y: { config: { rules: [{ id: "color-contrast", reviewOnFail: true }] } },
|
|
77
|
+
storyshots: { disable: true }
|
|
78
|
+
}
|
|
68
79
|
};
|
|
69
80
|
|
|
70
81
|
// Route sort logic story:
|
package/src/itinerary.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import polyline from "@mapbox/polyline";
|
|
2
2
|
import {
|
|
3
|
+
AppliedFareProduct,
|
|
3
4
|
Company,
|
|
4
5
|
Config,
|
|
6
|
+
Currency,
|
|
5
7
|
ElevationProfile,
|
|
6
8
|
ElevationProfileComponent,
|
|
7
9
|
FlexBookingInfo,
|
|
@@ -104,8 +106,6 @@ export function isFlex(leg: Leg): boolean {
|
|
|
104
106
|
legContainsGeometry(leg)
|
|
105
107
|
);
|
|
106
108
|
}
|
|
107
|
-
|
|
108
|
-
// alpha-only comment
|
|
109
109
|
export function isRideshareLeg(leg: Leg): boolean {
|
|
110
110
|
return !!leg.rideHailingEstimate?.provider?.id;
|
|
111
111
|
}
|
|
@@ -246,6 +246,9 @@ export function getCompanyFromLeg(leg: Leg): string {
|
|
|
246
246
|
if (from.rentalVehicle) {
|
|
247
247
|
return from.rentalVehicle.network;
|
|
248
248
|
}
|
|
249
|
+
if (from.vehicleRentalStation?.rentalNetwork) {
|
|
250
|
+
return from.vehicleRentalStation.rentalNetwork.networkId;
|
|
251
|
+
}
|
|
249
252
|
if (
|
|
250
253
|
(mode === "MICROMOBILITY" || mode === "SCOOTER") &&
|
|
251
254
|
rentedVehicle &&
|
|
@@ -606,45 +609,91 @@ export function getDisplayedStopId(placeOrStop: Place | Stop): string {
|
|
|
606
609
|
return stopCode || stopId?.split(":")[1] || stopId;
|
|
607
610
|
}
|
|
608
611
|
|
|
612
|
+
/**
|
|
613
|
+
* Removes the first part of the OTP standard scope (":"), if it is present
|
|
614
|
+
* @param item String that is potentially scoped with `:` character
|
|
615
|
+
* @returns descoped string
|
|
616
|
+
*/
|
|
617
|
+
export const descope = (item: string): string => item?.split(":")?.[1];
|
|
618
|
+
|
|
619
|
+
export type ExtendedMoney = Money & { originalAmount?: number };
|
|
620
|
+
|
|
621
|
+
export const zeroDollars = (currency: Currency): Money => ({
|
|
622
|
+
amount: 0,
|
|
623
|
+
currency
|
|
624
|
+
});
|
|
625
|
+
|
|
609
626
|
/**
|
|
610
627
|
* Extracts useful data from the fare products on a leg, such as the leg cost and transfer info.
|
|
611
|
-
* @param leg
|
|
612
|
-
* @param
|
|
613
|
-
* @param
|
|
614
|
-
* @
|
|
628
|
+
* @param leg Leg with Fares v2 information
|
|
629
|
+
* @param mediumId Desired medium ID to calculate fare for
|
|
630
|
+
* @param riderCategoryId Desire rider category to calculate fare for
|
|
631
|
+
* @param seenFareIds Fare IDs used on previous legs. Used to detect transfer discounts.
|
|
632
|
+
* @returns Object containing price as well as transfer/dependent
|
|
633
|
+
* fare information. `AppliedFareProduct` should contain
|
|
634
|
+
* all the information needed, but the other fields are kept to
|
|
635
|
+
* make the transition to Fares V2 less jarring.
|
|
615
636
|
*/
|
|
616
637
|
export function getLegCost(
|
|
617
638
|
leg: Leg,
|
|
618
639
|
mediumId: string | null,
|
|
619
|
-
riderCategoryId: string | null
|
|
640
|
+
riderCategoryId: string | null,
|
|
641
|
+
seenFareIds?: string[]
|
|
620
642
|
): {
|
|
643
|
+
alternateFareProducts?: AppliedFareProduct[];
|
|
644
|
+
appliedFareProduct?: AppliedFareProduct;
|
|
645
|
+
isDependent?: boolean;
|
|
621
646
|
price?: Money;
|
|
622
|
-
transferAmount?: Money | undefined;
|
|
623
647
|
productUseId?: string;
|
|
624
648
|
} {
|
|
625
649
|
if (!leg.fareProducts) return { price: undefined };
|
|
626
|
-
const relevantFareProducts = leg.fareProducts
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
650
|
+
const relevantFareProducts = leg.fareProducts
|
|
651
|
+
.filter(({ product }) => {
|
|
652
|
+
// riderCategory and medium can be specifically defined as null to handle
|
|
653
|
+
// generic GTFS based fares from OTP when there is no fare model
|
|
654
|
+
|
|
655
|
+
// Remove (optional) agency scoping
|
|
656
|
+
const productRiderCategoryId =
|
|
657
|
+
descope(product?.riderCategory?.id) ||
|
|
658
|
+
product?.riderCategory?.id ||
|
|
659
|
+
null;
|
|
660
|
+
|
|
661
|
+
const productMediaId =
|
|
662
|
+
descope(product?.medium?.id) || product?.medium?.id || null;
|
|
663
|
+
return (
|
|
664
|
+
productRiderCategoryId === riderCategoryId &&
|
|
665
|
+
productMediaId === mediumId &&
|
|
666
|
+
// Make sure there's a price
|
|
667
|
+
// Some fare products don't have a price at all.
|
|
668
|
+
product?.price
|
|
669
|
+
);
|
|
670
|
+
})
|
|
671
|
+
.map(fare => {
|
|
672
|
+
const alreadySeen = seenFareIds?.indexOf(fare.id) > -1;
|
|
673
|
+
const { currency } = fare.product.price;
|
|
674
|
+
return {
|
|
675
|
+
id: fare.id,
|
|
676
|
+
product: {
|
|
677
|
+
...fare.product,
|
|
678
|
+
legPrice: alreadySeen ? zeroDollars(currency) : fare.product.price
|
|
679
|
+
} as AppliedFareProduct
|
|
680
|
+
};
|
|
681
|
+
})
|
|
682
|
+
.sort((a, b) => a.product?.legPrice?.amount - b.product?.legPrice?.amount);
|
|
683
|
+
|
|
684
|
+
// Return the cheapest, but include other matches as well
|
|
685
|
+
const cheapestRelevantFareProduct = relevantFareProducts[0];
|
|
686
|
+
|
|
687
|
+
// TODO: return one object here instead of dumbing it down?
|
|
644
688
|
return {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
689
|
+
alternateFareProducts: relevantFareProducts.splice(1).map(fp => fp.product),
|
|
690
|
+
appliedFareProduct: cheapestRelevantFareProduct?.product,
|
|
691
|
+
isDependent:
|
|
692
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
693
|
+
cheapestRelevantFareProduct?.product.__typename ===
|
|
694
|
+
"DependentFareProduct",
|
|
695
|
+
price: cheapestRelevantFareProduct?.product.legPrice,
|
|
696
|
+
productUseId: cheapestRelevantFareProduct?.id
|
|
648
697
|
};
|
|
649
698
|
}
|
|
650
699
|
|
|
@@ -653,6 +702,7 @@ export function getLegCost(
|
|
|
653
702
|
* @param legs Itinerary legs with fare products (must have used getLegsWithFares)
|
|
654
703
|
* @param category Rider category (youth, regular, senior)
|
|
655
704
|
* @param container Fare container (cash, electronic)
|
|
705
|
+
* @param seenFareIds List of fare product IDs that have already been seen on prev legs.
|
|
656
706
|
* @returns Money object for the total itinerary cost.
|
|
657
707
|
*/
|
|
658
708
|
export function getItineraryCost(
|
|
@@ -660,22 +710,23 @@ export function getItineraryCost(
|
|
|
660
710
|
mediumId: string | null,
|
|
661
711
|
riderCategoryId: string | null
|
|
662
712
|
): Money | undefined {
|
|
663
|
-
const
|
|
713
|
+
const legCostsObj = legs
|
|
664
714
|
// Only legs with fares (no walking legs)
|
|
665
715
|
.filter(leg => leg.fareProducts?.length > 0)
|
|
666
716
|
// Get the leg cost object of each leg
|
|
667
717
|
.map(leg => getLegCost(leg, mediumId, riderCategoryId))
|
|
668
|
-
.filter(cost => cost.
|
|
718
|
+
.filter(cost => cost.appliedFareProduct?.legPrice !== undefined)
|
|
669
719
|
// Filter out duplicate use IDs
|
|
670
720
|
// One fare product can be used on multiple legs,
|
|
671
721
|
// and we don't want to count it more than once.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
722
|
+
// Use an object keyed by productUseId to deduplicate, then extract prices
|
|
723
|
+
.reduce<{ [productUseId: string]: Money }>((acc, cur) => {
|
|
724
|
+
if (cur.productUseId && acc[cur.productUseId] === undefined) {
|
|
725
|
+
acc[cur.productUseId] = cur.appliedFareProduct?.legPrice;
|
|
675
726
|
}
|
|
676
|
-
return
|
|
677
|
-
},
|
|
678
|
-
|
|
727
|
+
return acc;
|
|
728
|
+
}, {});
|
|
729
|
+
const legCosts = Object.values(legCostsObj);
|
|
679
730
|
|
|
680
731
|
if (legCosts.length === 0) return undefined;
|
|
681
732
|
// Calculate the total
|