@tomtom-org/maps-sdk 0.58.1 → 0.59.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.
@@ -1 +1 @@
1
- {"version":3,"file":"services.es.js","names":["geometrySchema","buildUrlBasePath","buildUrlBasePath","buildUrlBasePath","buildUrlBasePath","buildUrlBasePath","buildRequestHeaders","DEFAULT_ATTRIBUTES","buildUrlBasePath","geometrySchema","buildUrlBasePath","geometrySchema","buildUrlBasePath","buildUrlBasePath","buildUrlBasePath","TomTomConfig","areaAnalyticsMetricKeys","avoidableTypes","bboxFromCoordsArray","bboxFromGeoJSON","bboxOnlyIfWithArea","currentTypes","generateId","generateTomTomHeaders","geographyTypes","getPosition","getPositionStrict","getRoutePlanningLocationType","inputSectionTypesWithGuidance","isProxyCredentialsMode","mergeFromGlobal","plugTypes","poiCategories","poiCategoriesToID","poiIDsToCategories","toPointGeometry","trafficIncidentRequestCategories","views","z","countBy","isNil","maxBy","minBy","omit","partition","pick","uniqBy","bbox","booleanIntersects","APICode","SDKError","Error","constructor","message","service","issues","super","this","captureStackTrace","APIErrorCode","TOO_MANY_REQUESTS","FORBIDDEN","SDKServiceError","status","SDKAbortError","cause","name","parseDefaultResponseError","error","serviceName","data","errorText","buildResponseError","parseResponseError","signal","aborted","reason","fetchError","buildValidationError","commonServiceRequestSchema","object","apiKey","string","optional","commonBaseURL","customServiceBaseURL","language","formatZodIssue","issue","depth","path","length","join","code","unionErrors","map","unionError","i","nested","ValidationError","zodError","JSON","stringify","callService","async","params","template","customApiVersion","getAPIVersion","mergedParams","apiVersion","validateRequest","config","validation","schema","extend","shape","superRefine","ctx","addIssue","refinements","refinement","check","safeParse","success","validateRequestSchema","requestValidation","e","apiRequest","buildRequest","headers","onAPIRequest","apiResponse","sendRequest","onAPIResponse","parseResponse","FetchError","returnOrThrow","response","ok","json","errorBody","contentType","get","bodyUsed","statusText","includes","detailedError","text","sleep","ms","Promise","resolve","reject","onAbort","clearTimeout","timer","setTimeout","removeEventListener","addEventListener","once","fetchWithRetry","fetchFn","retryConfig","instance","retry","initialWaitMs","backoffFactor","timeoutMs","startTime","Date","now","backoffMs","retryAfterSeconds","Number","parseInt","waitMs","isFinite","catch","proxyModeCredentials","input","sdkHeaders","options","credentials","url","inputHeaders","URL","init","fetch","post","method","body","fetchWith","lineStringCoordsSchema","array","number","geometrySchema$1","type","enum","coordinates","union","radius","radiusMeters","refine","Boolean","featureSchema","literal","geometry","id","properties","unknown","featureCollectionSchema","features","hasLngLatSchema","tuple","min","max","geoJsonbBoxSchema","arr","GeoJSONSchema","hasBBoxSchema","commonPlacesParamsSchema","query","position","limit","extendedPostalCodesFor","mapcodes","view","searchExtraParamsOptional","indexes","poiBrands","connectors","fuelTypes","openingHours","timeZone","relatedPois","minPowerKW","maxPowerKW","minFuzzyLevel","maxFuzzyLevel","commonSearchParamsSchema","lineStringSchema","routeFeatureSchema","positionsArraySchema","routeSchema","alongRouteSearchRequestMandatory","route","maxDetourTimeSeconds","int","positive","alongRouteSearchRequestOptional","sortBy","alongRouteSearchRequestSchema","arrayToCSV","Array","isArray","String","sampleWithinMaxLength","maxLength","sampledArray","increment","Math","ceil","push","buildCredentialHeaders","buildCommonServiceRequestHeaders","appendCommonParams","urlParams","append","buildPlacesRequestHeaders","appendPlacesLanguageParam","appendByJoiningParamValue","values","appendOptionalParam","value","appendGeoBiasParams","geoBias","hasLngLat","appendLatLonParamsFromPosition","boundingBox","appendCommonSearchParams","searchUrl","searchParams","category","resolvePlacesEndpointUrl","endpoint","toChargingPointAvailability","chargingStations","availability","count","statusCounts","station","chargingPoint","chargingPoints","areEqual","connectorA","connectorB","ratedPowerKW","addConnectorCountAndStatus","availabilities","connector","existingAvailability","find","connectorAvailability","toConnectorCounts","counts","existingCount","connectorCount","addConnectorCount","toConnectorBasedAvailabilities","toChargingSpeed","powerInKW","csvLatLngToPosition","csv","splitLatLng","split","positionToCSVLatLon","apiToGeoJSONBBox","apiBBox","westSouth","eastNorth","topLeftPoint","hasTopLeftPoint","lon","btmRightPoint","lat","southWest","northEast","latLonAPIToPosition","point","parseMoment","momentApi","year","month","day","dateYyyymmdd","splitDate","parseYyyymmddDate","date","dateYYYYMMDD","hour","minute","alwaysOpenInThisPeriod","timeRanges","start","end","parseOpeningHours","openingHoursApi","timeRangeApi","endTime","mode","alwaysOpenThisPeriod","parseSearchAPIResult","result","entryPoints","poi","dist","chargingPark","rest","connectorType","chargingSpeed","distance","entrypoint","categories","categorySet","c","filter","localizedCategories","phone","brands","brand","parseSummaryAPI","summary","alongRouteSearchTemplate","encodeURIComponent","buildUrlBasePath$10","points","coord","results","alongRouteSearch","evChargingStationsAvailabilityRequestSchema","routeTypes","tollTransponderOptions","evChargingStationsAvailabilityTemplate","buildUrlBasePath$9","accessType","stations","chargingPointAvailability","connectorAvailabilities","apiError","errorMessage","evChargingStationsAvailability","getPlaceWithEVAvailability","place","commonParams","availabilityId","dataSources","chargingAvailability","console","getPlacesWithEVAvailability","places","excludeIfAvailabilityUnknown","enhancedPlaces","placeWithAvailability","hasChargingAvailability","fetchFirstPlace","functionName","queryOrParams","first","pointGeoBiasSchema","geoBiasSchema","commonGeocodeAndFuzzySearchParamsSchema","typeahead","boolean","offset","countries","geocodingRequestSchema","parseApiResult","addressRanges","entityType","geographyType","from","to","geocodingTemplate","buildUrlBasePath$8","geocode","geocodeOne","geometryDataRequestMandatory","geometries","geometryDataRequestOptional","zoom","geometryDataRequestSchema","getGeometryIDs","placesArray","geometryDataTemplate","buildUrlBasePath$7","geometryIDs","appendGeometries","additionalData","flatMap","geometryData","feature","geometryResult","placesIdMap","reduce","acc","geometryId","placeCoordinates","mergePlacesWithGeometries","placeDetailsRequestMandatory","entityId","placeDetailsRequestOptional","placeDetailsRequestSchema","placeDetailsTemplate","buildUrlBasePath$6","getPlaceDetails","cache","Map","textCache","DEFAULT_LANGUAGE","getWithDefault","normalizeText","toLowerCase","replaceAll","buildTextCache","texts","synonyms","poiCategoriesRequestSchema","filters","parsePoiCategory","api","childCategoryCodes","childCategoryIds","poiCategoriesTemplate","baseUrl","filterCategories","cachedTextEntries","getCachedTextEntries","resultsByCode","filterTerm","normalizedFilterInput","Object","entries","set","childCode","delete","getPOICategories","allCategories","getCachedCategories","lang","setCachedCategories","getPOICategoryCodes","parseRoutingResponseError","description","kWhFromPercentage","maxChargeKWH","percentage","isElectricVehicle","vehicle","engineType","electricEngineOf","model","engine","withParameter","parameters","speedToConsumptionString","rates","rate","speedKMH","consumptionUnitsPer100KM","efficiencyParams","efficiency","acceleration","deceleration","uphill","downhill","electricConsumptionParams","consumption","speedsToConsumptionsKWH","constantSpeedConsumptionInkWhPerHundredkm","auxiliaryPowerInkW","consumptionInKWHPerKMAltitudeGain","recuperationInKWHPerKMAltitudeLoss","combustionConsumptionParams","speedsToConsumptionsLiters","constantSpeedConsumptionInLitersPerHundredkm","auxiliaryPowerInLitersPerHour","fuelEnergyDensityInMJoulesPerLiter","maxChargeKWHOf","charging","maxChargeParams","maxChargeInkWh","currentChargeKWHOf","absoluteCharge","state","currentChargeInkWh","currentChargePCT","currentChargeParams","currentFuelParams","currentFuelInLiters","chargingPreferencesOf","preferences","chargingPreferences","setEnergyParams","positiveNumber","nonNegativeNumber","percentageNumber","optionalPositiveNumber","optionalNonNegativeNumber","optionalNormalizedNumber","speedToConsumptionRateSchema","baseConsumptionModelSchema","speedConsumptionArray","combustionConsumptionModelSchema","electricConsumptionModelSchema","chargingConnectorSchema","currentType","baseLoadInkW","maxPowerInkW","maxVoltageInV","maxCurrentInA","voltageRange","minVoltageInV","batteryCurveSchema","stateOfChargeInkWh","chargingModelSchema","batteryCurve","chargingConnectors","chargingTimeOffsetInSec","combustionEngineModelSchema","electricEngineModelSchema","vehicleDimensionsSchema","weightKG","predefinedVehicleModelSchema","variantId","explicitVehicleModelSchema","dimensions","vehicleModelSchema","genericVehicleStateSchema","heading","combustionVehicleStateSchema","electricVehicleStateByPercentageSchema","electricVehicleStateByKwhSchema","electricVehicleStateSchema","chargingPreferencesPCTSchema","minChargeAtDestinationPCT","minChargeAtChargingStopsPCT","chargingPreferencesKWHSchema","minChargeAtDestinationInkWh","minChargeAtChargingStopsInkWh","chargingPreferencesSchema","electricVehiclePreferencesSchema","baseVehicleParamsSchema","restrictions","maxSpeedKMH","tollTransponder","genericVehicleParamsSchema","undefined","combustionVehicleParamsSchema","electricVehicleParamsSchema","vehicleParametersSchema","discriminatedUnion","costModelSchema","avoid","traffic","routeType","avoidAreas","departArriveSchema","option","commonRoutingRequestSchema","costModel","travelMode","when","budgetTypes","reachableRangeRequestSchemaMandatory","origin","budget","reachableRangeAvoidableSchema","exclude","reachableRangeVehicleSchema","reachableRangeWhenSchema","reachableRangeCostModelSchema","reachableRangeRequestSchemaOptional","maxFerryLengthMeters","smoothing","reachableRangeRequestSchema","chargeBudgetTypes","Set","batteryCapacityRefinement","has","currentChargeRefinement","reachableRangeRequestValidationConfig","buildCommonRoutingBody","avoids","departureDateTime","toISOString","predefinedModelIdOf","buildCommonVehicleBody","vehicleEngineType","vehicleWeightInKilograms","vehicleMaxSpeedInKilometersPerHour","vehicleHasElectronicTollCollectionTransponder","appendVehicleEnergyParams","electricEngine","combustionEngine","combustionEngineOf","appendVehicleQueryParams","buildPostData","buildRequestHeaders$1","Attributes","propertyKeys","keys","reachableRangeTemplate","buildUrl","toString","currentChargeKWH","remainingKWH","appendBudget","reachableRange","boundary","longitude","latitude","calculateReachableRange","combineSignals","signals","live","AbortSignal","any","calculateReachableRanges","paramsArray","AREA_TYPE_BY_GEOGRAPHY_TYPE","Country","CountrySubdivision","CountrySecondarySubdivision","CountryTertiarySubdivision","Municipality","MunicipalitySubdivision","MunicipalitySecondarySubdivision","Neighbourhood","PostalCodeArea","ISO2_TO_ISO3","AF","AX","AL","DZ","AS","AD","AO","AI","AQ","AG","AR","AM","AW","AU","AT","AZ","BS","BH","BD","BB","BY","BE","BZ","BJ","BM","BT","BO","BQ","BA","BW","BV","BR","IO","BN","BG","BF","BI","CV","KH","CM","CA","KY","CF","TD","CL","CN","CX","CC","CO","KM","CG","CD","CK","CR","CI","HR","CU","CW","CY","CZ","DK","DJ","DM","DO","EC","EG","SV","GQ","ER","EE","SZ","ET","FK","FO","FJ","FI","FR","GF","PF","TF","GA","GM","GE","DE","GH","GI","GR","GL","GD","GP","GU","GT","GG","GN","GW","GY","HT","HM","VA","HN","HK","HU","IS","IN","ID","IR","IQ","IE","IM","IL","IT","JM","JP","JE","JO","KZ","KE","KI","KP","KR","KW","KG","LA","LV","LB","LS","LR","LY","LI","LT","LU","MO","MG","MW","MY","MV","ML","MT","MH","MQ","MR","MU","YT","MX","FM","MD","MC","MN","ME","MS","MA","MZ","MM","NA","NR","NP","NL","NC","NZ","NI","NE","NG","NU","NF","MK","MP","NO","OM","PK","PW","PS","PA","PG","PY","PE","PH","PN","PL","PT","PR","QA","RE","RO","RU","RW","BL","SH","KN","LC","MF","PM","VC","WS","SM","ST","SA","SN","RS","SC","SL","SG","SX","SK","SI","SB","SO","ZA","GS","SS","ES","LK","SD","SR","SJ","SE","CH","SY","TW","TJ","TZ","TH","TL","TG","TK","TO","TT","TN","TR","TM","TC","TV","UG","UA","AE","GB","US","UM","UY","UZ","VU","VE","VN","VG","VI","WF","EH","YE","ZM","ZW","toIso3","iso2","PLACE_TYPE_BY_RESULT_TYPE","street","area","address","GEOGRAPHY_TYPE_BY_AREA_TYPE","fromEntries","areaType","mapAddress","apiAddress","houseNumber","countryCodeIso2","streetName","streetNumber","countryCode","countryCodeISO3","revGeocodeRequestMandatory","revGeocodeRequestOptional","revGeocodeRequestSchema","reverseGeocodingTemplate","lng","buildUrlBasePath$5","firstResult","freeformAddress","title","accessPoints","accessPoint","originalPosition","reverseGeocode","EXPLICIT_SECTION_TYPES","buildAttributesHeader","parts","sectionTypes","wantAll","guidance","extendedRouteRepresentations","routesAttr","buildVehicleBody","vehicleHeadingInDegrees","appendLDEVRVehicleParams","absolutePreferences","percentagePreferences","chargingPreferenceParams","isLDEVRRequest","PREDEFINED_MODEL_WITHOUT_LDEVR_MESSAGE","expandPathToStops","pathLike","getPathCoordinates","legSections","sections","leg","getPathLegSections","every","section","startPointIndex","endPointIndex","stops","coordinate","legToNext","slice","at","getRouteStopOptions","location","toLegRequest","arrivingStop","routeStop","pauseDurationSeconds","pauseDurationInSeconds","candidateEntryPoints","entryPoint","preferredEntryPointIndex","legCostModel","appendTravelTimeParam","computeTravelTimeFor","waypointLikeSchema","pathLikeSchema","mandatorySchema","locations","optionalSchema","arrivalSide","chargingStopsStrategy","maxAlternatives","routeRequestValidationConfig","types","waypointCount","pathCount","destination","iconCategoryByCategory","other","accident","fog","danger","rain","frost","jam","roadworks","wind","flooding","iconCategoryLookup","categoryByIconCategory","delayMagnitudeLookup","minor","moderate","major","toIconCategory","toTrafficIncidentCategory","iconCategory","toDelayMagnitude","apiMagnitude","toCurrentType","apiCurrentType","toChargingStop","chargingInformationAtEndOfLeg","chargingConnectionInfo","chargingParkLocation","addressParts","chargingParkId","city","municipality","region","countrySubdivision","postalCode","country","plugType","chargingPlugType","currentInA","chargingCurrentInA","voltageInV","chargingVoltageInV","chargingPowerInkW","chargingCurrentType","targetChargeInPCT","targetChargeInkWh","chargingParkPowerInkW","chargingParkSpeed","parseSummary","apiSummary","lengthInMeters","travelTimeInSeconds","travelDurationInSeconds","trafficDelayInSeconds","trafficDelayDurationInSeconds","trafficLengthInMeters","historicTrafficTravelTimeInSeconds","liveTrafficIncidentsTravelTimeInSeconds","noTrafficTravelTimeInSeconds","departureTime","arrivalTime","arrivalDateTime","deviationDistanceInMeters","deviationTimeInSeconds","deviationDurationInSeconds","deviationPoint","fuelConsumptionInLiters","totalChargingTimeInSeconds","batteryConsumptionInkWh","batteryConsumptionInKilowattHours","batteryConsumption","batteryConsumptionInPCT","remainingChargeAtArrivalInkWh","remainingChargeAtArrivalInPCT","parseLegSectionProps","apiLegs","accumulator","legIndex","legCoordinateCount","lastEndIndex","stopTimeInSeconds","nextLeg","arrival","getTime","nextDeparture","isNaN","stopSeconds","round","parseLegStopTime","originalWaypointIndexAtEndOfLeg","originalWaypointIndex","toSectionProps","startPathIndex","endPathIndex","parseSections","apiRoute","legs","addBasic","key","items","carTrain","ferry","tunnel","motorway","pedestrian","toll","tollRoad","carpool","urban","unpaved","lowEmissionZone","countryCodeISO2","vehicleRestricted","travelModeSection","delayInSeconds","delayDurationInSeconds","effectiveSpeedInKmh","effectiveSpeedInKilometersPerHour","tec","causes","mainCauseCode","magnitudeOfDelay","delayMagnitude","eventId","speedLimit","speedRestrictions","r","inKilometersPerHour","maxSpeedLimitInKmh","lanes","lanesSection","lane","directions","direction","LANE_DIRECTION_MAP","follow","laneSeparators","separator","LANE_SEPARATOR_MAP","roadShields","roadShield","roadShieldReferences","tollVignette","importantRoadStretch","index","importantRoadStretchIndex","roadNumbers","roadNumber","requested","applyRequestedSectionFilter","DELTA","similar","pointA","pointB","abs","MANEUVER_MAP","depart","waypointLeft","waypointRight","waypointReached","waypointAhead","arrive","arriveLeft","arriveRight","arriveAhead","continueStraight","keepLeft","keepRight","turnSlightRight","turnRight","turnSharpRight","turnSlightLeft","turnLeft","turnSharpLeft","makeUTurn","roundaboutStraight","roundaboutSharpRight","roundaboutRight","roundaboutSlightRight","roundaboutSlightLeft","roundaboutLeft","roundaboutSharpLeft","roundaboutBack","exitRoundabout","exitMotorwayLeft","exitMotorwayRight","switchMotorwayLeft","switchMotorwayRight","enterCarpoolLaneLeft","enterCarpoolLaneRight","exitCarpoolLaneLeft","exitCarpoolLaneRight","mergeLeftLane","mergeRightLane","takeShipFerry","takeCarTrain","leaveShipFerry","leaveCarTrain","crossBorder","passTollgate","ROAD_PROPERTY_MAP","controlledAccess","LANDMARK_MAP","endOfRoad","atTrafficLight","onToBridge","onBridge","afterBridge","intoTunnel","insideTunnel","afterTunnel","TOLL_PAYMENT_MAP","cashCoinsAndBills","cashBillsOnly","cashCoinsOnly","cashExactChange","creditCard","debitCard","travelCard","etc","etcTransponder","etcVideoCamera","subscription","straight","slightRight","right","sharpRight","rightUTurn","slightLeft","left","sharpLeft","leftUTurn","noMarking","longDashed","doubleSolid","singleSolid","solidDashed","dashedSolid","shortDashed","shadedAreaMarking","dashedBlocks","doubleDashed","crossingAlert","physicalDivider","physicalDividerLessThan3m","physicalDividerGuardrail","curb","MANEUVER_ANGLE_MAP","back","ROUNDABOUT_TYPE_MAP","regular","small","SIDE_ROAD_SIDE_MAP","leftAndRight","EMPTY_ROAD_INFO","toInstructionText","instructionText","parseInstructionRoadInformation","road","p","toUpperCase","roadNames","identifier","s","countrySubdivisionCodeIso","stateCode","iconReference","roadShieldReference","parseTollAndCrossing","a","tollgateName","tollPaymentTypes","t","countryCrossingFromName","countryCrossingFromCodeIso2","countryCrossingFromCode","countryCrossingToName","countryCrossingToCodeIso2","countryCrossingToCode","toManeuverAngle","angle","parseManeuverExtras","intersectionName","drivingSide","landmark","distanceToPreviousTrafficLightInMeters","trafficLightOffsetInMeters","sideRoads","sideRoad","side","offsetFromManeuverInMeters","isDrivable","maneuverView","onRouteAngle","offRouteAngles","roundaboutType","isManeuverObligatory","changeOfAngleInDegrees","ambiguousExitOffsetFromManeuverInMeters","offsetOfAmbiguousExitFromManeuverInMeters","roundaboutExitNumber","parseRoadShieldFields","roadShieldAtlasReference","signpost","exitName","exitNumber","towardName","parseSignpost","nextRoadInformation","ref","exitIconReference","signpostRoadShieldReferences","parseInstruction","maneuverPoint","pathPointIndex","routeOffsetInMeters","maneuver","routePath","rp","distanceInMeters","distanceFromRouteStartInMeters","travelTimeFromRouteStartInSeconds","nextRoadInfo","previousRoadInfo","previousRoadInformation","parseGuidance","instructions","parsed","lastPathIndex","apiInstruction","pathIndex","calculateRouteTemplate","hasPathLocations","allStops","useEntryPoint","stopEntryPoint","flattenLocations","useEntryPoints","routePlanningLocations","middleStops","waypoints","stop","buildRoutePlanningLocations","commonBodyFields","maxPathAlternativeRoutes","instructionPhonetics","rectangles","rectangle","minLon","minLat","maxLon","maxLat","arrivalSidePreference","buildCommonBodyFields","buildRequestHeaders","buildLegs","sharedBody","ldevrRequest","baseURL","chargingModel","chargingParameters","buildLDEVRRequest","routes","progressPoints","progress","progressPoint","pointIndex","parseRoute","calculateRoute","fuzzySearchRequestOptional","fuzzySearchRequestSchema","queryIntentApiToSdk","intentApi","intent","details","fuzzySearchTemplate","buildUrlBasePath$4","queryIntent","geometrySearchRequestMandatory","geometrySearchRequestSchema","sdkGeometryToApiGeometries","searchGeometry","vertices","polygonSizeMap","forEach","polygon","bboxOfPolygon","polygonSize","sort","b","splice","findFiftyLargestPolygons","polygonCoords","geometrySearchTemplate","mergedOptions","geometryList","discoverPlaces","geometrySearch","fuzzySearch","discoverOnePlace","autocompleteSearchRequestMandatory","autocompleteSearchRequestOptional","resultTypes","autocompleteSearchRequestSchema","autocompleteSearchTemplate","context","segments","segment","isCategorySegment","autocompleteSearch","words","normalize","replace","namesOf","matchedAlternativeName","findConceptSegment","candidates","some","target","nameWords","_","startsWith","startsAtWordOf","queryWord","nameWord","coversMostOfWordIn","blendSuggestions","concept","maxResults","distinctPlaces","ordered","titleWords","titleWord","titleMatches","flat","suggestion","SERVICE_NAME","REFINEMENT_KINDS","isRefinementKind","kind","INDEXES_BY_PLACE_KIND","KIND_BY_PLACE_TYPE","POI","Street","Geography","toRadians","degrees","PI","distanceFrom","lng1","lat1","lng2","lat2","halfChord","sin","cos","asin","sqrt","metersBetween","toPlaceSuggestions","originPosition","homeCountryCode","homeCountryCodeOf","addressLines","subtitles","foreignCountry","toPlaceSuggestion","throwAsSearchSuggestionsError","toRefinement","scope","refineParams","shared","toRefinementSuggestions","pickConcept","searchCarriesBrand","getSearchSuggestions","placeKinds","refinementKinds","wantsPlaces","wantsRefinements","kinds","splitKinds","originBias","placesBias","poiTypes","areaTypes","searchResponse","autocompleteResponse","all","autocompleteResults","suggestions","ONE_DAY_MS","addDays","days","setUTCDate","getUTCDate","daysAgo","toDateString","startOfDayUTC","parse","OFFSET","parseDateTime","dateTime","test","functionalRoadClasses","API_NAMES_BY_METRIC_KEY","speed","dataType","field","freeFlowSpeed","congestionLevel","travelTime","networkLength","ALL_HOURS","frcToNumber","frc","indexOf","parseMetricFields","parseValue","metricKey","apiValue","parseMetrics","parseMetricKey","parsePeriodStart","time","getUTCFullYear","getUTCMonth","getUTCHours","IDENTIFIERS_BY_GRANULARITY","yearly","monthly","weekly","thursday","getUTCDay","dayOfYear","UTC","week","floor","parseISOWeek","daily","hourly","parseCalendarEntries","granularity","entry","DAY_BY_API_DAY_OF_WEEK","MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY","parseAverageEntry","apiDay","hasOwn","hourOfWeek","parseTileEntry","tile","tileCentre","parseAnomaly","startDate","endDate","labels","parseTimedData","average","second","parseAnomalies","anomalies","parseFeature","apiFeature","timezone","level","baseData","timedData","tiledData","tiles","computeTileRanges","f","ranges","v","parseAnalysisPeriod","times","dateInputSchema","regex","geometrySchema","isAccepted","trafficAreaAnalyticsTemplate","metrics","hours","hasDays","hasStart","diffDays","buildUrlBasePath$1","datePart","dataTypes","frcs","heatmap","trafficAreaAnalytics","DEFAULT_ATTRIBUTES","timeValidity","probabilityOfOccurrence","numberOfReports","lastReportTime","property","parseEvent","apiEvent","parseIncident","apiIncident","events","withoutBBox","incidentsIntersecting","incidents","toGeometries","incident","incidentBBox","part","bboxesIntersect","positionSchema","linearRingSchema","polygonCoordinatesSchema","polygonGeometrySchema","polygonFeatureSchema","polygonSchema","trafficIncidentDetailsTemplate","ids","categoryFilter","timeValidityFilter","buildUrlBasePath","appendOptionalParams","buildHeaders","resolvedBBox","recompute","trafficIncidentDetails"],"sources":["../src/shared/types/apiResponseErrorTypes.ts","../src/shared/errors.ts","../src/shared/schema/commonParamsSchema.ts","../src/shared/schema/validation.ts","../src/shared/serviceTemplate.ts","../src/shared/fetch.ts","../src/shared/schema/geometriesSchema.ts","../src/shared/schema/commonPlacesParamsSchema.ts","../src/search/commonSearchParamsSchema.ts","../src/along-route-search/alongRouteSearchRequestSchema.ts","../src/shared/arrays.ts","../src/shared/request/requestBuildingUtils.ts","../src/shared/request/commonSearchRequestBuilder.ts","../src/shared/request/placesEndpoint.ts","../src/along-route-search/requestBuilder.ts","../src/ev-charging-stations-availability/connectorAvailability.ts","../src/shared/ev.ts","../src/shared/geometry.ts","../src/shared/searchResultParsing.ts","../src/along-route-search/responseParser.ts","../src/along-route-search/alongRouteSearchTemplate.ts","../src/along-route-search/alongRouteSearch.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityRequestSchema.ts","../src/shared/types/commonRoutingParams.ts","../src/shared/types/vehicleRestrictionParams.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityResponseErrorParser.ts","../src/ev-charging-stations-availability/requestBuilder.ts","../src/ev-charging-stations-availability/responseParser.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityTemplate.ts","../src/ev-charging-stations-availability/evChargingStationsAvailability.ts","../src/shared/firstPlace.ts","../src/shared/schema/geoBiasSchema.ts","../src/shared/schema/commonGeocodeAndFuzzySearchParamsSchema.ts","../src/geocode/geocodingRequestSchema.ts","../src/geocode/requestBuilder.ts","../src/geocode/responseParser.ts","../src/geocode/geocodingTemplate.ts","../src/geocode/geocoding.ts","../src/geometry-data/geometryDataRequestSchema.ts","../src/geometry-data/requestBuilder.ts","../src/geometry-data/responseParser.ts","../src/geometry-data/geometryDataTemplate.ts","../src/geometry-data/geometryData.ts","../src/place-details/placeDetailsSchema.ts","../src/place-details/requestBuilder.ts","../src/place-details/responseParser.ts","../src/place-details/placeDetailsTemplate.ts","../src/place-details/getPlaceDetails.ts","../src/poi-categories/cache.ts","../src/poi-categories/poiCategoriesRequestSchema.ts","../src/poi-categories/requestBuilder.ts","../src/poi-categories/responseParser.ts","../src/poi-categories/poiCategoriesTemplate.ts","../src/poi-categories/poiCategories.ts","../src/routing/routingResponseErrorParser.ts","../src/shared/request/vehicleEnergyParams.ts","../src/shared/schema/vehicleParamsSchema.ts","../src/shared/schema/commonRoutingRequestSchema.ts","../src/reachable-range/types/reachableRangeParams.ts","../src/reachable-range/reachableRangeRequestSchema.ts","../src/shared/request/routingBodyBuilder.ts","../src/reachable-range/requestBuilder.ts","../src/reachable-range/responseParser.ts","../src/reachable-range/reachableRangeTemplate.ts","../src/reachable-range/calculateReachableRange.ts","../src/revgeo/requestBuilder.ts","../src/shared/iso2ToIso3.ts","../src/revgeo/responseParser.ts","../src/revgeo/revGeocodeRequestSchema.ts","../src/revgeo/reverseGeocodingTemplate.ts","../src/revgeo/reverseGeocoding.ts","../src/routing/requestBuilder.ts","../src/routing/types/calculateRouteParams.ts","../src/routing/calculateRouteRequestSchema.ts","../src/shared/trafficIncidents.ts","../src/routing/responseParser.ts","../src/routing/calculateRouteTemplate.ts","../src/routing/calculateRoute.ts","../src/fuzzy-search/fuzzySearchRequestSchema.ts","../src/fuzzy-search/requestBuilder.ts","../src/fuzzy-search/responseParser.ts","../src/fuzzy-search/fuzzySearchTemplate.ts","../src/fuzzy-search/fuzzySearch.ts","../src/geometry-search/geometrySearchRequestSchema.ts","../src/geometry-search/requestBuilder.ts","../src/geometry-search/responseParser.ts","../src/geometry-search/geometrySearchTemplate.ts","../src/geometry-search/geometrySearch.ts","../src/search/discoverPlaces.ts","../src/autocomplete-search/autocompleteSearchRequestSchema.ts","../src/autocomplete-search/requestBuilder.ts","../src/autocomplete-search/responseParser.ts","../src/autocomplete-search/autocompleteSearchTemplate.ts","../src/autocomplete-search/autocompleteSearch.ts","../src/search-suggestions/blend.ts","../src/search-suggestions/getSearchSuggestions.ts","../src/traffic-area-analytics/dates.ts","../src/traffic-area-analytics/types/trafficAreaAnalyticsParams.ts","../src/traffic-area-analytics/requestBuilder.ts","../src/traffic-area-analytics/responseParser.ts","../src/traffic-area-analytics/trafficAreaAnalyticsRequestSchema.ts","../src/traffic-area-analytics/trafficAreaAnalyticsTemplate.ts","../src/traffic-area-analytics/trafficAreaAnalytics.ts","../src/traffic-incident-details/requestBuilder.ts","../src/traffic-incident-details/responseParser.ts","../src/traffic-incident-details/trafficIncidentDetailsRequestSchema.ts","../src/traffic-incident-details/trafficIncidentDetailsTemplate.ts","../src/traffic-incident-details/trafficIncidentDetails.ts"],"sourcesContent":["/**\n * @ignore\n */\nexport interface BaseAPIResponseErrorBody {\n /*\n * HTTP error code.\n */\n httpStatusCode: number;\n /*\n * Detailed information about the error.\n */\n detailedError: {\n /*\n * One of the defined error codes.\n */\n code: string;\n /*\n * A human-readable representation of the error code.\n */\n message: string;\n /*\n * Target of the particular error.\n * Value: The name of the request parameter.\n */\n target: string;\n };\n}\n\n/**\n * @ignore\n */\nexport interface DefaultAPIResponseErrorBody extends BaseAPIResponseErrorBody {\n /*\n * A human-readable description of the error code.\n */\n error?: string;\n errorText?: string;\n}\n\n/**\n * @ignore\n */\nexport interface RoutingAPIResponseError {\n /*\n * \tThe format version\n */\n formatVersion: string;\n error: {\n /*\n * A human-readable representation of the error code.\n */\n description: string;\n };\n detailedError: {\n /*\n * A human-readable representation of the error code.\n */\n message: string;\n /*\n * One of the defined error codes.\n */\n code: string;\n };\n}\n\n/**\n * Error returned by an API.\n * @ignore\n */\nexport type APIErrorResponse<T = DefaultAPIResponseErrorBody> = {\n status?: number;\n message: string;\n data?: T;\n};\n\n/**\n * @ignore\n */\nexport enum APICode {\n TOO_MANY_REQUESTS = 429,\n FORBIDDEN = 403,\n}\n","import type { ZodIssue } from 'zod';\nimport type { ValidationError } from './schema/validation';\nimport type { ParseResponseError } from './serviceTypes';\nimport type { APIErrorResponse, DefaultAPIResponseErrorBody } from './types/apiResponseErrorTypes';\nimport { APICode } from './types/apiResponseErrorTypes';\nimport type { ServiceName } from './types/servicesTypes';\n\n/**\n * Base error class for all SDK-related errors.\n *\n * The SDK handles two distinct categories of errors:\n * 1. **Programming errors**: Configuration or usage errors in the user's application,\n * such as passing incorrect types to parameters or functions.\n * 2. **API errors**: Recoverable errors that occur during SDK operations,\n * such as network failures or invalid API responses.\n *\n * @example\n * ```typescript\n * try {\n * // SDK operation\n * } catch (error) {\n * if (error instanceof SDKError) {\n * console.error(`Error in ${error.service}: ${error.message}`);\n * }\n * }\n * ```\n *\n * @group Errors\n */\nexport class SDKError extends Error {\n /**\n * Creates a new SDKError instance.\n *\n * @param message - Human-readable error description\n * @param service - Name of the service where the error occurred\n * @param issues - Optional array of Zod validation issues for detailed error information\n */\n constructor(\n message: string,\n readonly service: string,\n readonly issues?: ZodIssue[],\n ) {\n super(message);\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SDKError);\n }\n }\n}\n\n/**\n * Mapping of API error codes to human-readable error messages.\n *\n * Provides standardized error messages for common API response codes,\n * ensuring consistent error handling across the SDK.\n *\n * @group Errors\n */\nexport const APIErrorCode: { readonly [K in APICode as number]: string } = {\n [APICode.TOO_MANY_REQUESTS]: 'Too Many Requests: The API Key is over QPS (Queries per second)',\n [APICode.FORBIDDEN]: 'Request failed with status code 403',\n};\n\n/**\n * Error class for HTTP API response errors.\n *\n * Extends {@link SDKError} to include HTTP status codes and automatically\n * maps known error codes to user-friendly messages using {@link APIErrorCode}.\n *\n * @example\n * ```typescript\n * throw new SDKServiceError('Invalid request', 'geocoding', 400);\n * ```\n *\n * @group Errors\n */\nexport class SDKServiceError extends SDKError {\n /**\n * HTTP status code of the failed API request.\n *\n * Common values:\n * - `400`: Bad Request\n * - `403`: Forbidden\n * - `429`: Too Many Requests\n * - `500`: Internal Server Error\n */\n status?: number;\n\n /**\n * Creates a new SDKServiceError instance.\n *\n * If the status code matches a known error in {@link APIErrorCode},\n * the message will be automatically replaced with the standardized message.\n *\n * @param message - Error message from the API or custom message\n * @param service - Name of the service that generated the error\n * @param status - HTTP status code of the failed request\n */\n constructor(message: string, service: string, status?: number) {\n super(message, service);\n this.status = status;\n\n /*\n * We use as message what returns from API if any, otherwise we have our APIErrorCode as a fallback\n * Check if there is a status and if the status exists in the mapped API error types\n */\n if (this.status && APIErrorCode[this.status]) {\n this.message = APIErrorCode[this.status];\n }\n }\n}\n\n/**\n * Error class for cancelled service calls.\n *\n * Thrown when the `signal` passed to a service call is aborted, either before the\n * request is sent or while it is in flight. Its `name` is `'AbortError'`, matching\n * the convention used by `fetch`, and the original abort reason — including a custom\n * value passed to `AbortController.abort(reason)` — is available on `cause`.\n *\n * @example\n * ```typescript\n * try {\n * await discoverPlaces({ query: 'cafe', signal: controller.signal });\n * } catch (error) {\n * if (error instanceof SDKAbortError) return; // superseded by a newer search\n * throw error;\n * }\n * ```\n *\n * @group Errors\n */\nexport class SDKAbortError extends SDKError {\n /**\n * Creates a new SDKAbortError instance.\n *\n * @param service - Name of the service whose call was cancelled\n * @param cause - The abort reason carried by the signal\n */\n constructor(service: string, cause?: unknown) {\n super('The operation was aborted.', service);\n this.name = 'AbortError';\n this.cause = cause;\n }\n}\n\n/**\n * @ignore\n * @param error\n * @param serviceName\n */\nexport const parseDefaultResponseError: ParseResponseError<DefaultAPIResponseErrorBody> = (error, serviceName) => {\n const { data, message, status } = error;\n // Different services uses property error or errorText or detailedError\n // Here we cover all situations as a default error parser\n const errorMessage = data?.error || data?.errorText || message;\n return new SDKServiceError(errorMessage, serviceName, status);\n};\n\n/**\n * @ignore\n * Generate error for APIResponse, any other error type will be returned as it is.\n * @param error The error captured by a catch function.\n * @param serviceName The name of the service.\n * @param parseResponseError\n * @param signal The signal given to the call, used to recognise a cancellation.\n */\nexport const buildResponseError = (\n error: unknown,\n serviceName: ServiceName,\n parseResponseError?: ParseResponseError<unknown>,\n signal?: AbortSignal,\n): SDKError => {\n // Checked before the status: an aborted fetch carries no status. Not a bare `signal?.aborted`\n // — `parseResponse` runs in the same `try`, so a real error thrown while the signal happens to\n // be aborted must not be reported as a cancellation. fetch rejects with exactly `signal.reason`.\n if (signal?.aborted && error === signal.reason) {\n return new SDKAbortError(serviceName, signal.reason);\n }\n\n // A custom `sendRequest` may mint its own AbortError instead.\n if (signal?.aborted && (error as Error)?.name === 'AbortError') {\n return new SDKAbortError(serviceName, signal.reason);\n }\n\n if ((error as APIErrorResponse).status) {\n const fetchError = error as APIErrorResponse;\n if (parseResponseError) {\n return parseResponseError(fetchError, serviceName);\n }\n return parseDefaultResponseError(fetchError, serviceName);\n }\n\n return new SDKError((error as Error).message, serviceName);\n};\n\n/**\n * @ignore\n * @param error\n * @param serviceName\n */\nexport const buildValidationError = (error: ValidationError, serviceName: ServiceName): SDKError =>\n new SDKError(error.message, serviceName, error.issues);\n","import { z } from 'zod';\n\n/**\n * @ignore\n */\nexport const commonServiceRequestSchema = z.object({\n apiKey: z.string().optional(),\n commonBaseURL: z.string().optional(),\n customServiceBaseURL: z.string().optional(),\n language: z.string().optional(),\n});\n","import type { ZodError } from 'zod';\nimport type { CommonServiceParams } from '../serviceTypes';\nimport type { RequestValidationConfig } from '../types/validation';\nimport { commonServiceRequestSchema } from './commonParamsSchema';\n\ntype ZodIssue = ZodError['issues'][number];\n\n/**\n * Format a Zod error into a human-readable string\n * @ignore\n */\nconst formatZodIssue = (issue: ZodIssue, depth = 0): string => {\n const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : '';\n if (issue.code === 'invalid_union') {\n const unionErrors = (issue as ZodIssue & { unionErrors?: ZodError[] }).unionErrors;\n if (unionErrors?.length) {\n const branches = unionErrors\n .map((unionError, i) => {\n const branchIssues = unionError.issues\n .map((nested) => formatZodIssue(nested, depth + 1))\n .join(', ');\n return `option[${i}]: { ${branchIssues} }`;\n })\n .join(' | ');\n return `${path}no matching union member (${branches})`;\n }\n }\n return `${path}${issue.message}`;\n};\n\nconst formatZodError = (error: ZodError): string => {\n return error.issues.map((issue) => formatZodIssue(issue)).join('; ');\n};\n\n/**\n * Validate Error Class for validating params input, this will be used by SDKError class.\n * @ignore\n */\nexport class ValidationError extends Error {\n issues: ZodError['issues'];\n\n constructor(zodError: ZodError) {\n const message = formatZodError(zodError);\n const issuesSummary = JSON.stringify(zodError.issues, null, 2);\n super(`${message}\\nIssues: ${issuesSummary}`);\n this.issues = zodError.issues;\n }\n}\n\n/**\n * @ignore\n * @param params\n * @param config\n */\nexport const validateRequestSchema = <T extends CommonServiceParams>(\n params: T,\n config?: RequestValidationConfig,\n): T => {\n const requestSchema = config?.schema\n ? commonServiceRequestSchema.extend(config.schema.shape)\n : commonServiceRequestSchema;\n\n // Apply all refinements using superRefine for better type compatibility in Zod v4\n const finalSchema = requestSchema.superRefine((data, ctx) => {\n // Validate common params\n if (!('commonBaseURL' in data) && !('customServiceBaseURL' in data)) {\n ctx.addIssue({\n code: 'custom',\n message: 'commonBaseURL or customServiceBaseURL is required',\n });\n }\n\n // Apply optional refinements\n if (config?.refinements?.length) {\n for (const refinement of config.refinements) {\n if (!refinement.check(data as T)) {\n ctx.addIssue({\n code: 'custom',\n message: refinement.message,\n });\n }\n }\n }\n });\n\n const validation = finalSchema.safeParse(params);\n if (!validation.success) {\n throw new ValidationError(validation.error);\n }\n\n return params;\n};\n","import { generateTomTomHeaders, mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\nimport { buildResponseError, buildValidationError, SDKAbortError } from './errors';\nimport type { ValidationError } from './schema/validation';\nimport { validateRequestSchema } from './schema/validation';\nimport type { CommonServiceParams, ServiceTemplate } from './serviceTypes';\nimport type { ServiceName } from './types/servicesTypes';\n\n/**\n * @ignore\n * Template execution of a service call.\n * Any service goes through the same template steps:\n * 1- Build request\n * 2- Send request and get API response\n * 3- Parse and return API response\n * @param params The parameters for that specific service call.\n * @param template The implementation of the template steps.\n * @param serviceName The name of the service.\n */\nexport const callService = async <PARAMS extends CommonServiceParams, ApiRequest, ApiResponse, RESPONSE>(\n params: PARAMS,\n template: ServiceTemplate<PARAMS, ApiRequest, ApiResponse, RESPONSE>,\n serviceName: ServiceName,\n): Promise<RESPONSE> => {\n // Bail before doing any work, so a superseded call never fires onAPIRequest\n if (params.signal?.aborted) {\n throw new SDKAbortError(serviceName, params.signal.reason);\n }\n\n const customApiVersion = template.getAPIVersion?.(params);\n const mergedParams = mergeFromGlobal({ ...params, ...(customApiVersion && { apiVersion: customApiVersion }) });\n // (params.validateRequest defaults to true, thus true and undefined are the same)\n if (params.validateRequest === undefined || params.validateRequest) {\n try {\n validateRequestSchema<PARAMS>(mergedParams, template.requestValidation);\n } catch (e) {\n throw buildValidationError(e as ValidationError, serviceName);\n }\n }\n const apiRequest = template.buildRequest(mergedParams);\n const headers = generateTomTomHeaders(mergedParams);\n params.onAPIRequest?.(apiRequest);\n\n try {\n const apiResponse = await template.sendRequest(apiRequest, headers, { signal: params.signal });\n params.onAPIResponse?.(apiRequest, apiResponse);\n return template.parseResponse(await apiResponse.data, mergedParams);\n } catch (e) {\n params.onAPIResponse?.(apiRequest, e);\n throw buildResponseError(e, serviceName, template.parseResponseError, params.signal);\n }\n};\n","import { isProxyCredentialsMode, RetryConfig, TomTomConfig, type TomTomHeaders } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, GetObject, ParsedFetchResponse, PostObject, RequestOptions } from './types/fetch';\n\n/**\n * Custom error class for HTTP fetch errors.\n */\nclass FetchError extends Error {\n public readonly status: number;\n public readonly data?: unknown;\n\n constructor(status: number, message?: string, data?: unknown) {\n super(message ?? `HTTP Error ${status}`);\n this.name = 'FetchError';\n this.status = status;\n this.data = data;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FetchError);\n }\n }\n}\n\n// Returns the response as a JSON object or throws an error if the response isn't successful.\nconst returnOrThrow = async <T>(response: Response): ParsedFetchResponse<T> => {\n if (response.ok) {\n return { data: await response.json(), status: response.status };\n }\n let message: string | undefined;\n let errorBody;\n const contentType = response.headers.get('content-type');\n if (response.bodyUsed) {\n message = response.statusText;\n } else if (contentType?.includes('application/json')) {\n errorBody = await response.json();\n message = errorBody?.errorText ?? errorBody?.message ?? errorBody?.detailedError?.message;\n } else if (contentType?.includes('text/xml')) {\n errorBody = await response.text();\n message = response.statusText;\n }\n\n throw new FetchError(response.status, message, errorBody);\n};\n\n// Waits for the given duration, rejecting with the abort reason if the signal fires.\nconst sleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise((resolve, reject) => {\n // addEventListener never fires for an abort that already happened\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\n/**\n * Wraps a fetch call with automatic retry on 429 (Too Many Requests) responses.\n * Respects the Retry-After header when present, otherwise uses exponential backoff.\n * A given signal cancels both the in-flight request and any pending backoff wait.\n * @ignore\n */\nconst fetchWithRetry = async (fetchFn: () => Promise<Response>, signal?: AbortSignal): Promise<Response> => {\n const retryConfig = TomTomConfig.instance.get().retry;\n if (!retryConfig) {\n return fetchFn();\n }\n\n const { initialWaitMs, backoffFactor, timeoutMs } = retryConfig as Required<RetryConfig>;\n\n const startTime = Date.now();\n let backoffMs = initialWaitMs;\n let response = await fetchFn();\n\n while (response.status === 429) {\n const retryAfterSeconds = Number.parseInt(response.headers.get('retry-after') ?? '', 10);\n const waitMs = Number.isFinite(retryAfterSeconds) ? retryAfterSeconds * 1000 : backoffMs;\n\n if (Date.now() - startTime + waitMs > timeoutMs) {\n break;\n }\n\n // Drain the body to free the connection\n await response.text().catch(() => {});\n\n await sleep(waitMs, signal);\n backoffMs *= backoffFactor;\n response = await fetchFn();\n }\n\n return response;\n};\n\nconst proxyModeCredentials = (): RequestCredentials | undefined => (isProxyCredentialsMode() ? 'include' : undefined);\n\n/**\n * Fetches the given HTTP JSON resource with an HTTP GET request and returns a promise with the response as a JSON object.\n * If the response isn't successful, it returns a rejected promise with the http error code.\n * @ignore\n * @param input A URL or a GET object with URL and optional service-specific headers.\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n * @param options Per-call HTTP options, such as the `AbortSignal` used to cancel the request.\n */\nexport const get = async <T>(\n input: URL | GetObject,\n sdkHeaders: TomTomHeaders,\n options?: RequestOptions,\n): ParsedFetchResponse<T> =>\n returnOrThrow(\n await fetchWithRetry(() => {\n const credentials = proxyModeCredentials();\n const { url, headers: inputHeaders } = input instanceof URL ? { url: input, headers: undefined } : input;\n const init: RequestInit = { headers: { ...sdkHeaders, ...inputHeaders } };\n if (credentials) init.credentials = credentials;\n if (options?.signal) init.signal = options.signal;\n\n return fetch(url, init);\n }, options?.signal),\n );\n\n/**\n * Fetches the given HTTP JSON resource with an HTTP POST request and returns a promise with the response as a JSON object.\n * If the response isn't successful, it returns a rejected promise with the http error code.\n * @ignore\n * @param input The POST object with URL, optional payload, and optional service-specific headers.\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n * @param options Per-call HTTP options, such as the `AbortSignal` used to cancel the request.\n */\nexport const post = async <T, D>(\n input: PostObject<D>,\n sdkHeaders: TomTomHeaders,\n options?: RequestOptions,\n): ParsedFetchResponse<T> =>\n returnOrThrow(\n await fetchWithRetry(() => {\n const credentials = proxyModeCredentials();\n const init: RequestInit = {\n method: 'POST',\n body: JSON.stringify(input.data),\n headers: { ...sdkHeaders, ...input.headers, 'Content-Type': 'application/json' },\n };\n if (credentials) init.credentials = credentials;\n if (options?.signal) init.signal = options.signal;\n\n return fetch(input.url, init);\n }, options?.signal),\n );\n\n/**\n * Fetches the given HTTP JSON resource with the given HTTP operation and URL/Payload as applicable.\n * * Useful for services which can use different HTTP methods depending on the parameters.\n * @param input The input object (e.g. containing either GET or POST data)\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n * @param options Per-call HTTP options, such as the `AbortSignal` used to cancel the request.\n * @ignore\n */\nexport const fetchWith = async <T, D = void>(\n input: FetchInput<D>,\n sdkHeaders: TomTomHeaders,\n options?: RequestOptions,\n): ParsedFetchResponse<T> => {\n if (input.method === 'GET') return get<T>(input, sdkHeaders, options);\n if (input.method === 'POST') return post<T, D>(input, sdkHeaders, options);\n throw new Error(`Unsupported HTTP method received: ${(input as { method: string }).method}`);\n};\n","import { z } from 'zod';\n\n/**\n * @ignore\n */\nexport const lineStringCoordsSchema = z.array(z.array(z.number()));\n\n/**\n * @ignore\n */\nexport const geometrySchema = z\n .object({\n type: z.enum([\n 'Point',\n 'MultiPoint',\n 'LineString',\n 'MultiLineString',\n 'Polygon',\n 'MultiPolygon',\n 'GeometryCollection',\n 'Circle',\n ]),\n coordinates: z.union([\n z.array(z.number()),\n lineStringCoordsSchema,\n z.array(z.array(z.array(z.number()))),\n z.array(z.array(z.array(z.array(z.number())))),\n ]),\n radius: z.optional(z.number()),\n radiusMeters: z.optional(z.number()),\n bbox: z.optional(z.array(z.number())),\n })\n .check(\n z.refine(\n (data) => (data.type === 'Circle' ? Boolean(data.radius) : true),\n 'type: \"Circle\" must have radius property',\n ),\n );\n\n/**\n * @ignore\n */\nexport const featureSchema = z.object({\n type: z.literal('Feature'),\n geometry: geometrySchema,\n id: z.optional(z.union([z.string(), z.number()])),\n properties: z.unknown().optional(),\n bbox: z.optional(z.array(z.number())),\n});\n\n/**\n * @ignore\n */\nexport const featureCollectionSchema = z.object({\n type: z.literal('FeatureCollection'),\n features: z.array(featureSchema),\n id: z.optional(z.union([z.string(), z.number()])),\n properties: z.unknown().optional(),\n bbox: z.optional(z.array(z.number())),\n});\n\n/**\n * @ignore\n */\nexport const hasLngLatSchema = z.union([\n z.tuple([z.number().min(-180).max(180), z.number().min(-90).max(90)]),\n z.tuple([z.number().min(-180).max(180), z.number().min(-90).max(90), z.number()]),\n z.object({\n type: z.literal('Point'),\n coordinates: z.array(z.number()),\n }),\n featureSchema,\n]);\n\n/**\n * @ignore\n */\nconst geoJsonbBoxSchema = z.union([\n z.array(z.number()).refine((arr) => arr.length === 4, { message: 'BBox must have 4 elements' }),\n z.array(z.number()).refine((arr) => arr.length === 6, { message: 'BBox must have 6 elements' }),\n]);\n\n/**\n * @ignore\n */\nexport const GeoJSONSchema = z.union([geometrySchema, featureSchema, featureCollectionSchema]);\n\n/**\n * @ignore\n */\nexport const hasBBoxSchema = z.union([geoJsonbBoxSchema, GeoJSONSchema, z.array(GeoJSONSchema)]);\n","import { views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { hasLngLatSchema } from './geometriesSchema';\n\n/**\n * @ignore\n */\nexport const commonPlacesParamsSchema = z.object({\n query: z.string().optional(),\n position: hasLngLatSchema.optional(),\n limit: z.number().max(100).optional(),\n extendedPostalCodesFor: z.array(z.string()).optional(),\n mapcodes: z.array(z.string()).optional(),\n view: z.enum(views).optional(),\n geographyTypes: z.array(z.string()).optional(),\n});\n","import { poiCategories } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonPlacesParamsSchema } from '../shared/schema/commonPlacesParamsSchema';\n\nconst searchExtraParamsOptional = z.object({\n indexes: z.array(z.string()).optional(),\n poiCategories: z.array(z.enum(poiCategories)).optional(),\n poiBrands: z.array(z.string()).optional(),\n connectors: z.array(z.string()).optional(),\n fuelTypes: z.array(z.string()).optional(),\n openingHours: z.string().optional(),\n timeZone: z.string().optional(),\n relatedPois: z.string().optional(),\n minPowerKW: z.number().optional(),\n maxPowerKW: z.number().optional(),\n minFuzzyLevel: z.number().optional(),\n maxFuzzyLevel: z.number().optional(),\n});\n\n/**\n * @ignore\n */\nexport const commonSearchParamsSchema = commonPlacesParamsSchema.extend(searchExtraParamsOptional.shape);\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { lineStringCoordsSchema } from '../shared/schema/geometriesSchema';\n\nconst lineStringSchema = z.object({\n type: z.literal('LineString'),\n coordinates: lineStringCoordsSchema,\n});\n\nconst routeFeatureSchema = z.object({\n type: z.literal('Feature'),\n geometry: lineStringSchema,\n});\n\nconst positionsArraySchema = lineStringCoordsSchema;\n\nconst routeSchema = z.union([lineStringSchema, routeFeatureSchema, positionsArraySchema]);\n\nconst alongRouteSearchRequestMandatory = z.object({\n route: routeSchema,\n maxDetourTimeSeconds: z.number().int().positive(),\n});\n\nconst alongRouteSearchRequestOptional = z.object({\n sortBy: z.enum(['detourTime', 'detourOffset']).optional(),\n});\n\n/**\n * @ignore\n */\nexport const alongRouteSearchRequestSchema = commonSearchParamsSchema.extend({\n ...alongRouteSearchRequestMandatory.shape,\n ...alongRouteSearchRequestOptional.shape,\n});\n","/**\n * @ignore\n * @param input\n */\nexport const arrayToCSV = (input: unknown | unknown[]): string =>\n !input ? '' : Array.isArray(input) ? input.join(',') : typeof input === 'string' ? input : String(input);\n\n/**\n * Samples, if necessary, the given array to fit within the given max length.\n * * If the array already fits within maxLength, the same array is returned.\n * * The sampling is done by spreading the array points at a constant increment.\n * * The first and last points are always included if array and max lengths are > 1.\n * * The sampling is done with speed in mind, so it's not guaranteed to always fit maxLength. Very often it will be shorter.\n *\n * Use this when needing to quickly simplify an array to fit some max length without caring too much on the lost detail.\n * * If both the array and max length are long enough, the loss of detail won't likely be an issue.\n * @ignore\n */\nexport const sampleWithinMaxLength = <T>(array: T[], maxLength: number): T[] => {\n const length = array.length;\n if (length <= maxLength) {\n return array;\n }\n\n const sampledArray = [];\n let i;\n const increment = Math.ceil(length / maxLength);\n for (i = 0; i < length; i += increment) {\n sampledArray.push(array[i]);\n }\n // ensuring the last point is always added:\n if (maxLength > 1 && i >= length - increment) {\n if (sampledArray.length < maxLength) {\n sampledArray.push(array[length - 1]);\n } else {\n sampledArray[sampledArray.length - 1] = array[length - 1];\n }\n }\n\n return sampledArray;\n};\n","import type { HasLngLat, InternalGlobalConfig, POICategory, TomTomAPIHeaders } from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON, getPosition, poiCategoriesToID } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport { arrayToCSV } from '../arrays';\nimport type { CommonServiceParams } from '../serviceTypes';\nimport type { GeoBias } from '../types/geoBias';\n\n/**\n * The credential pair: the API key and the API version.\n *\n * @remarks\n * The `apiKey` guard is what makes proxy deployments work: with no key configured nothing is\n * emitted, and `fetch` attaches `credentials: 'include'` so the proxy can inject the real one.\n */\nconst buildCredentialHeaders = (params: Partial<InternalGlobalConfig>): TomTomAPIHeaders => ({\n ...(params.apiKey && { 'TomTom-Api-Key': params.apiKey }),\n ...(!isNil(params.apiVersion) && { 'TomTom-Api-Version': String(params.apiVersion) }),\n});\n\n/**\n * Builds the TomTom API request headers common to every service: the credential pair plus the\n * language. Service request builders spread the result and add any service-specific headers\n * (e.g. `Attributes`) on top.\n * @ignore\n */\nexport const buildCommonServiceRequestHeaders = (params: Partial<InternalGlobalConfig>): TomTomAPIHeaders => ({\n ...buildCredentialHeaders(params),\n ...(params.language && { 'Accept-Language': params.language }),\n});\n\n/**\n * @ignore\n * @param urlParams\n * @param params\n */\nexport const appendCommonParams = (urlParams: URLSearchParams, params: Partial<InternalGlobalConfig>): void => {\n urlParams.append('apiVersion', String(params.apiVersion));\n if (params.apiKey) {\n urlParams.append('key', params.apiKey);\n }\n params.language && urlParams.append('language', params.language);\n};\n\n/**\n * The credential headers for a places request: the API key and the API version, and nothing else.\n *\n * @remarks\n * Deliberately **not** {@link buildCommonServiceRequestHeaders}, which also emits\n * `Accept-Language`. The `apiVersion=1` places endpoints ignore that header — a request carrying it\n * comes back in the default language, with no error. Only `language=` in the query string\n * localizes, so {@link appendPlacesLanguageParam} keeps it there. Probed against production; the\n * API documentation does not state it.\n *\n * `reverseGeocode` is the exception and uses the common builder, because it is pinned to\n * `apiVersion: 2`, where the header is honoured.\n *\n * @ignore\n */\nexport const buildPlacesRequestHeaders = (params: Partial<InternalGlobalConfig>): TomTomAPIHeaders =>\n buildCredentialHeaders(params);\n\n/**\n * Appends the query parameters a places request still carries in the URL.\n *\n * @remarks\n * Only `language`. The key and the version moved to headers — see\n * {@link buildPlacesRequestHeaders} for why this one could not follow them.\n *\n * @ignore\n */\nexport const appendPlacesLanguageParam = (urlParams: URLSearchParams, params: CommonServiceParams): void => {\n params.language && urlParams.append('language', params.language);\n};\n\n/**\n * Adds parameter from the array by repeating each array part into a query parameter of the same name.\n * E.g. ...&avoid=motorways&avoid=ferries&...\n * @ignore\n * @param urlParams\n * @param paramName\n * @param paramArray\n */\nexport const appendByRepeatingParamName = (\n urlParams: URLSearchParams,\n paramName: string,\n paramArray?: string[],\n): void => {\n for (const param of paramArray || []) {\n urlParams.append(paramName, param);\n }\n};\n\n/**\n * @ignore\n */\nexport const appendByJoiningParamValue = (\n urlParams: URLSearchParams,\n name: string,\n values?: string[] | number[] | (string | number)[],\n): void => {\n if (Array.isArray(values) && values.length > 0) {\n urlParams.append(name, values.join(','));\n }\n};\n\n/**\n * @ignore\n */\nexport const appendOptionalParam = (\n urlParams: URLSearchParams,\n name: string,\n value?: string | number | boolean,\n): void => {\n !isNil(value) && urlParams.append(name, String(value));\n};\n\n/**\n * Adds lat and lon parameters to the url.\n * @ignore\n * @param urlParams\n * @param hasLngLat\n */\nexport const appendLatLonParamsFromPosition = (urlParams: URLSearchParams, hasLngLat: HasLngLat | undefined): void => {\n const position = getPosition(hasLngLat);\n if (position) {\n urlParams.append('lat', String(position[1]));\n urlParams.append('lon', String(position[0]));\n }\n};\n\n/**\n * Adds whichever geographic bias was given: `lat`/`lon` plus `radius` for a point, or the\n * `topLeft`/`btmRight` corner pair for a bounding box.\n *\n * @ignore\n */\nexport const appendGeoBiasParams = (urlParams: URLSearchParams, geoBias: GeoBias | undefined): void => {\n if (!geoBias) return;\n\n if (geoBias.position !== undefined) {\n appendLatLonParamsFromPosition(urlParams, geoBias.position);\n appendOptionalParam(urlParams, 'radius', geoBias.radiusMeters);\n\n return;\n }\n\n const bbox = bboxFromGeoJSON(geoBias.boundingBox);\n if (!bbox) return;\n\n urlParams.append('topLeft', arrayToCSV([bbox[3], bbox[0]]));\n urlParams.append('btmRight', arrayToCSV([bbox[1], bbox[2]]));\n};\n\n/**\n * Map POICategory values to their numeric IDs for use in API requests.\n * @ignore\n */\nexport const mapPOICategoriesToIDs = (categories: POICategory[]): number[] =>\n categories.map((category) => poiCategoriesToID[category]);\n","import type { AlongRouteSearchParams } from '../../along-route-search';\nimport type { FuzzySearchParams } from '../../fuzzy-search';\nimport type { GeometrySearchParams } from '../../geometry-search';\nimport {\n appendByJoiningParamValue,\n appendOptionalParam,\n appendPlacesLanguageParam,\n mapPOICategoriesToIDs,\n} from './requestBuildingUtils';\n\n/**\n * Appends request parameters common to search APIs such as fuzzy + geometry search.\n * * Mutates the given searchURL with the appended parameters.\n * @param searchUrl The search URL to append parameters to. Should come without any parameters at this point.\n * @param params The search parameters, with global configuration already merged into them.\n */\nexport const appendCommonSearchParams = (\n searchUrl: URL,\n params: FuzzySearchParams | GeometrySearchParams | AlongRouteSearchParams,\n): void => {\n const urlParams = searchUrl.searchParams;\n appendPlacesLanguageParam(urlParams, params);\n appendOptionalParam(urlParams, 'limit', params.limit);\n appendByJoiningParamValue(urlParams, 'fuelSet', params.fuelTypes);\n appendByJoiningParamValue(urlParams, 'idxSet', params.indexes);\n appendByJoiningParamValue(urlParams, 'brandSet', params.poiBrands);\n params.poiCategories &&\n appendByJoiningParamValue(urlParams, 'categorySet', mapPOICategoriesToIDs(params.poiCategories));\n appendByJoiningParamValue(urlParams, 'connectorSet', params.connectors);\n appendByJoiningParamValue(urlParams, 'mapcodes', params.mapcodes);\n appendByJoiningParamValue(urlParams, 'extendedPostalCodesFor', params.extendedPostalCodesFor);\n\n appendOptionalParam(urlParams, 'minPowerKW', params.minPowerKW);\n appendOptionalParam(urlParams, 'maxPowerKW', params.maxPowerKW);\n appendOptionalParam(urlParams, 'view', params.view);\n appendOptionalParam(urlParams, 'openingHours', params.openingHours);\n appendOptionalParam(urlParams, 'timeZone', params.timeZone);\n appendOptionalParam(urlParams, 'relatedPois', params.relatedPois);\n appendByJoiningParamValue(urlParams, 'entityTypeSet', params.geographyTypes);\n};\n","import type { CommonServiceParams } from '../serviceTypes';\n\n/**\n * The path every places endpoint hangs off.\n *\n * @ignore\n */\nexport const PLACES_URL_PATH = '/maps/orbis/places';\n\n/**\n * The URL for one places endpoint, honouring a caller's `customServiceBaseURL` override.\n *\n * @remarks\n * `endpoint` is the path below {@link PLACES_URL_PATH}, already interpolated where it carries a\n * query — `'search/pizza.json'`, `'poiCategories.json'`, `'ev/id'`.\n *\n * An **empty** `customServiceBaseURL` counts as unset — hence `||` rather than `??`, since\n * `new URL('')` throws. One resolver, so every places service answers the same way.\n *\n * @ignore\n */\nexport const resolvePlacesEndpointUrl = (params: CommonServiceParams, endpoint: string): string =>\n params.customServiceBaseURL || `${params.commonBaseURL}${PLACES_URL_PATH}/${endpoint}`;\n","import type { LineString } from 'geojson';\nimport { appendCommonSearchParams } from '../shared/request/commonSearchRequestBuilder';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { appendOptionalParam, buildPlacesRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { AlongRouteSearchParams, AlongRouteSearchRequestAPI } from './types';\n\nconst toLineString = (route: AlongRouteSearchParams['route']): LineString => {\n if (Array.isArray(route)) {\n return { type: 'LineString', coordinates: route };\n }\n return route.type === 'Feature' ? route.geometry : route;\n};\n\nconst buildUrlBasePath = (params: AlongRouteSearchParams): string =>\n resolvePlacesEndpointUrl(params, `searchAlongRoute/${encodeURIComponent(params.query ?? '')}.json`);\n\n/**\n * Default function for building an along-route search request from {@link AlongRouteSearchParams}.\n * @param params The along-route search parameters, with global configuration already merged into them.\n */\nexport const buildAlongRouteSearchRequest = (params: AlongRouteSearchParams): AlongRouteSearchRequestAPI => {\n const url = new URL(buildUrlBasePath(params));\n appendCommonSearchParams(url, params);\n const urlParams = url.searchParams;\n urlParams.append('maxDetourTime', String(params.maxDetourTimeSeconds));\n appendOptionalParam(urlParams, 'sortBy', params.sortBy);\n appendOptionalParam(urlParams, 'spreadingMode', 'plan');\n\n return {\n url,\n headers: buildPlacesRequestHeaders(params),\n data: {\n route: {\n points: toLineString(params.route).coordinates.map((coord) => ({\n lat: coord[1],\n lon: coord[0],\n })),\n },\n },\n };\n};\n","import type {\n ChargingPointAvailability,\n ChargingPointStatus,\n ChargingStation,\n Connector,\n ConnectorAvailability,\n ConnectorCount,\n} from '@tomtom-org/maps-sdk/core';\n\n/**\n * @ignore\n */\nexport const toChargingPointAvailability = (chargingStations: ChargingStation[]): ChargingPointAvailability => {\n const availability: ChargingPointAvailability = { count: 0, statusCounts: {} };\n for (const station of chargingStations) {\n for (const chargingPoint of station.chargingPoints) {\n availability.count++;\n availability.statusCounts[chargingPoint.status] =\n (availability.statusCounts[chargingPoint.status] || 0) + 1;\n }\n }\n return availability;\n};\n\n// Two connectors can be considered equal when they have the same type and power:\nconst areEqual = (connectorA: Connector, connectorB: Connector) =>\n connectorA.type === connectorB.type && connectorA.ratedPowerKW === connectorB.ratedPowerKW;\n\nconst addConnectorCount = (\n connectors: Connector[] | undefined,\n counts: ConnectorCount[], // we are mutating this input\n): void => {\n if (!connectors) {\n // defensive check, sometimes connectors are undefined\n return;\n }\n for (const connector of connectors) {\n const existingCount = counts.find((connectorCount) => areEqual(connector, connectorCount.connector));\n if (existingCount) {\n existingCount.count++;\n } else {\n // new count entry:\n counts.push({ connector, count: 1 });\n }\n }\n};\n\nconst addConnectorCountAndStatus = (\n connectors: Connector[] | undefined,\n status: ChargingPointStatus | undefined,\n availabilities: ConnectorAvailability[], // we are mutating this input\n): void => {\n if (!connectors) {\n // defensive check, sometimes connectors are undefined\n return;\n }\n for (const connector of connectors) {\n const existingAvailability = availabilities.find((connectorAvailability) =>\n areEqual(connector, connectorAvailability.connector),\n );\n if (existingAvailability) {\n existingAvailability.count++;\n if (status) {\n // we're mutating the input object here:\n const statusCounts = existingAvailability.statusCounts;\n existingAvailability.statusCounts[status] = (statusCounts[status] || 0) + 1;\n }\n } else {\n // new availability entry:\n availabilities.push({ connector, count: 1, statusCounts: status ? { [status]: 1 } : {} });\n }\n }\n};\n\n/**\n * @ignore\n */\nexport const toConnectorCounts = (connectors: Connector[]): ConnectorCount[] => {\n const availabilities: ConnectorAvailability[] = [];\n addConnectorCount(connectors, availabilities);\n return availabilities;\n};\n\n/**\n * @ignore\n */\nexport const toConnectorBasedAvailabilities = (chargingStations: ChargingStation[]): ConnectorAvailability[] => {\n const availabilities: ConnectorAvailability[] = [];\n for (const station of chargingStations) {\n for (const chargingPoint of station.chargingPoints) {\n addConnectorCountAndStatus(chargingPoint.connectors, chargingPoint.status, availabilities);\n }\n }\n return availabilities;\n};\n","import { ChargingSpeed } from '@tomtom-org/maps-sdk/core';\n\n/**\n * @ignore\n */\nexport const toChargingSpeed = (powerInKW: number): ChargingSpeed => {\n if (powerInKW < 12) {\n return 'slow';\n } else if (powerInKW < 50) {\n return 'regular';\n } else if (powerInKW < 150) {\n return 'fast';\n }\n return 'ultra-fast';\n};\n","import type { BBox } from '@tomtom-org/maps-sdk/core';\nimport type { Position } from 'geojson';\nimport type { BoundingBoxAPI, BoundingBoxTopLeftAPI, LatLonAPI } from './types/apiPlacesResponseTypes';\n\n/**\n * @ignore\n * @param csv\n */\nexport const csvLatLngToPosition = (csv: string): Position => {\n const splitLatLng = csv.split(',');\n return [Number(splitLatLng[1]), Number(splitLatLng[0])];\n};\n\n/**\n * @ignore\n * @param position\n */\nexport const positionToCSVLatLon = (position: Position): string => `${position[1]},${position[0]}`;\n\nconst hasTopLeftPoint = (bbox: BoundingBoxAPI): bbox is BoundingBoxTopLeftAPI => {\n return (<BoundingBoxTopLeftAPI>bbox).topLeftPoint !== undefined;\n};\n\n/**\n * @ignore\n * @param apiBBox\n */\nexport const apiToGeoJSONBBox = (apiBBox: BoundingBoxAPI): BBox => {\n let westSouth: Position;\n let eastNorth: Position;\n if (hasTopLeftPoint(apiBBox)) {\n westSouth = [apiBBox.topLeftPoint.lon, apiBBox.btmRightPoint.lat];\n eastNorth = [apiBBox.btmRightPoint.lon, apiBBox.topLeftPoint.lat];\n } else {\n westSouth = csvLatLngToPosition(apiBBox.southWest);\n eastNorth = csvLatLngToPosition(apiBBox.northEast);\n }\n return [westSouth[0], westSouth[1], eastNorth[0], eastNorth[1]];\n};\n\n/**\n * @ignore\n * @param point\n */\nexport const latLonAPIToPosition = (point: LatLonAPI): Position => {\n return [point.lon, point.lat];\n};\n","import type { Moment, OpeningHours, Place, POICategory, SearchPlaceProps, TimeRange } from '@tomtom-org/maps-sdk/core';\nimport { poiIDsToCategories, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { omit } from 'lodash-es';\nimport { toConnectorCounts } from '../ev-charging-stations-availability/connectorAvailability';\nimport { toChargingSpeed } from './ev';\nimport { apiToGeoJSONBBox, latLonAPIToPosition } from './geometry';\nimport type {\n BrandAPI,\n CategoryAPI,\n CommonSearchPlaceResultAPI,\n MomentAPI,\n OpeningHoursAPI,\n SummaryAPI,\n} from './types/apiPlacesResponseTypes';\nimport type { SearchSummary } from './types/searchSummary';\n\nconst parseYyyymmddDate = (dateYyyymmdd: string): { year: number; month: number; day: number } => {\n const splitDate = dateYyyymmdd.split('-');\n return {\n year: Number.parseInt(splitDate[0]),\n month: Number.parseInt(splitDate[1]),\n day: Number.parseInt(splitDate[2]),\n };\n};\n\nconst parseMoment = (momentApi: MomentAPI): Moment => {\n const { year, month, day } = parseYyyymmddDate(momentApi.date);\n return {\n dateYYYYMMDD: momentApi.date,\n year,\n month,\n day,\n hour: momentApi.hour,\n minute: momentApi.minute,\n date: new Date(year, month - 1, day, momentApi.hour, momentApi.minute),\n };\n};\n\nconst alwaysOpenInThisPeriod = (timeRanges: TimeRange[]): boolean =>\n timeRanges.length === 1 && timeRanges[0].start.hour === 0 && timeRanges[0].end.hour === 0;\n\n/**\n * @ignore\n */\nexport const parseOpeningHours = (openingHoursApi: OpeningHoursAPI): OpeningHours => {\n const timeRanges = openingHoursApi.timeRanges.map(\n (timeRangeApi): TimeRange => ({\n start: parseMoment(timeRangeApi.startTime),\n end: parseMoment(timeRangeApi.endTime),\n }),\n );\n return {\n mode: openingHoursApi.mode,\n timeRanges,\n alwaysOpenThisPeriod: alwaysOpenInThisPeriod(timeRanges),\n };\n};\n\n/**\n * Shared response parsing between geometry search and place by id service.\n * @ignore\n */\nexport const parseSearchAPIResult = (result: CommonSearchPlaceResultAPI): Place<SearchPlaceProps> => {\n const { position, entryPoints, poi, id, dist, boundingBox, chargingPark, ...rest } = result;\n const connectors = chargingPark?.connectors?.map((connector) => ({\n ...omit(connector, 'connectorType'),\n type: connector.connectorType,\n chargingSpeed: toChargingSpeed(connector.ratedPowerKW),\n }));\n return {\n type: 'Feature',\n geometry: toPointGeometry(latLonAPIToPosition(position)),\n ...(boundingBox && { bbox: apiToGeoJSONBBox(boundingBox) }),\n id,\n properties: {\n ...omit(rest, 'viewport'),\n ...(dist && { distance: dist }),\n ...(entryPoints?.length && {\n entryPoints: entryPoints.map((entrypoint) => ({\n ...entrypoint,\n position: latLonAPIToPosition(entrypoint.position),\n })),\n }),\n ...(connectors?.length && {\n chargingPark: {\n ...chargingPark,\n connectors: toConnectorCounts(connectors),\n },\n }),\n ...(poi && {\n poi: {\n name: poi.name,\n categories: (\n poi.categorySet?.map((c: CategoryAPI): POICategory => poiIDsToCategories[c.id]) ?? []\n ).filter((c: POICategory) => !!c),\n localizedCategories: poi.categories ?? [],\n ...(poi.phone && { phone: poi.phone }),\n ...(poi.brands && { brands: poi.brands.map((brand: BrandAPI) => brand.name) }),\n ...(poi.url && { url: poi.url }),\n ...(poi.openingHours && { openingHours: parseOpeningHours(poi.openingHours) }),\n ...(poi.timeZone && { timeZone: poi.timeZone }),\n },\n }),\n },\n };\n};\n\n/**\n * @ignore\n */\nexport const parseSummaryAPI = (summary: SummaryAPI): SearchSummary => {\n const { geoBias, ...rest } = summary;\n\n return {\n ...(geoBias && { geoBias: latLonAPIToPosition(geoBias) }),\n ...rest,\n };\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { AlongRouteSearchResponse, AlongRouteSearchResponseAPI } from './types';\n\n/**\n * Default function to parse an along-route search response.\n * @param apiResponse The API response.\n */\nexport const parseAlongRouteSearchResponse = (apiResponse: AlongRouteSearchResponseAPI): AlongRouteSearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { post } from '../shared/fetch';\nimport { alongRouteSearchRequestSchema } from './alongRouteSearchRequestSchema';\nimport { buildAlongRouteSearchRequest } from './requestBuilder';\nimport { parseAlongRouteSearchResponse } from './responseParser';\nimport type {\n AlongRouteSearchParams,\n AlongRouteSearchRequestAPI,\n AlongRouteSearchResponse,\n AlongRouteSearchResponseAPI,\n} from './types';\n\n/**\n * Along-route search service template type.\n * @ignore\n */\nexport type AlongRouteSearchTemplate = ServiceTemplate<\n AlongRouteSearchParams,\n AlongRouteSearchRequestAPI,\n AlongRouteSearchResponseAPI,\n AlongRouteSearchResponse\n>;\n\n/**\n * Along-route search service template main implementation.\n * @ignore\n */\nexport const alongRouteSearchTemplate: AlongRouteSearchTemplate = {\n requestValidation: { schema: alongRouteSearchRequestSchema },\n buildRequest: buildAlongRouteSearchRequest,\n sendRequest: post,\n parseResponse: parseAlongRouteSearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport { alongRouteSearchTemplate } from './alongRouteSearchTemplate';\nimport type { AlongRouteSearchParams, AlongRouteSearchResponse } from './types';\n\n/**\n * Search for places along a route, ranked by detour cost.\n *\n * Returns POIs reachable within a given detour budget from the provided route geometry.\n * Results are sorted by detour time (or offset along the route) so the most\n * accessible stops appear first.\n *\n * @remarks\n * This service is ideal for trip planning use cases:\n * - Find fuel or EV charging stations along the way\n * - Locate food stops or rest areas near the route\n * - Surface services reachable within a time budget\n *\n * The `route` is typically sourced from a {@link calculateRoute} result.\n *\n * @param params Along-route search parameters including the route geometry and detour budget\n *\n * @returns Promise resolving to a collection of matching places sorted by detour cost\n *\n * @example\n * ```typescript\n * // Find coffee shops within a 5-minute detour\n * const results = await alongRouteSearch({\n * route: routeResult.features[0].geometry,\n * maxDetourTimeSeconds: 300,\n * query: 'coffee',\n * });\n *\n * // EV chargers with a 10-minute budget, sorted by position on route\n * const chargers = await alongRouteSearch({\n * route: routeResult.features[0].geometry,\n * maxDetourTimeSeconds: 600,\n * poiCategories: ['CHARGING_LOCATION'],\n * sortBy: 'detourOffset',\n * });\n * ```\n *\n * @see [Along Route Search API Documentation](https://docs.tomtom.com/search-api/documentation/tomtom-orbis-maps/search-service/along-route-search)\n *\n * @ignore (exposed via 'search')\n */\nexport const alongRouteSearch = async (params: AlongRouteSearchParams): Promise<AlongRouteSearchResponse> =>\n callService(params, alongRouteSearchTemplate, 'AlongRouteSearch');\n","import { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\n\n/**\n * @ignore\n */\nexport const evChargingStationsAvailabilityRequestSchema = commonServiceRequestSchema.extend({\n id: z.string(),\n});\n","import type { Avoidable, HasBBox, TravelMode } from '@tomtom-org/maps-sdk/core';\nimport { VehicleParameters } from './vehicleParams';\n\n/**\n * Available route types, where each type specifies the type of optimization used when calculating routes:\n * * **fast**: Route calculation is optimized by travel time, while keeping the routes sensible. For example, the calculation may avoid shortcuts along inconvenient side roads or long detours that only save very little time.\n * * **short**: Route calculation is optimized such that a good compromise between small travel time and short travel distance is achieved.\n * * **efficient**: Route calculation is optimized such that a good compromise between small travel time and low fuel or energy consumption is achieved.\n * * **thrilling**: Route calculation is optimized such that routes include interesting or challenging roads and use as few motorways as possible.\n * There is a limit of 900km on routes planned with routeType=thrilling.\n *\n * @group Routing\n */\nexport const routeTypes = ['fast', 'short', 'efficient', 'thrilling'] as const;\n\n/**\n * Route optimization strategy for route calculation.\n *\n * Determines what the routing engine optimizes for when calculating the route.\n * Each type produces different routes suited to different use cases.\n *\n * @remarks\n * **Route Type Strategies:**\n *\n * - **`fast`**: Minimize travel time while maintaining practicality\n * - Prefers major roads and highways\n * - Avoids unnecessary detours and shortcuts on minor roads\n * - Best for most everyday use cases (commuting, business travel)\n *\n * - **`short`**: Balance between time and distance\n * - Good compromise between speed and mileage\n * - May use smaller roads to save distance\n * - Useful for short trips or when fuel costs matter\n *\n * - **`efficient`**: Minimize fuel or energy consumption\n * - Optimizes for least energy use\n * - Considers vehicle consumption model\n * - Avoids rapid acceleration/deceleration\n * - Best used with vehicle consumption parameters\n *\n * - **`thrilling`**: Scenic and engaging routes\n * - Prefers curvy, interesting roads\n * - Minimizes motorway usage\n * - **Limited to 900km maximum route length**\n * - Ideal for motorcycle rides or scenic drives\n *\n * @example\n * ```typescript\n * // Fastest route for commuting\n * const routeType: RouteType = 'fast';\n *\n * // Most fuel-efficient route for long trip\n * const ecoRoute: RouteType = 'efficient';\n *\n * // Scenic route for leisure\n * const scenicRoute: RouteType = 'thrilling';\n * ```\n *\n * @group Routing\n */\nexport type RouteType = (typeof routeTypes)[number];\n\n/**\n * Traffic consideration mode for route calculation.\n *\n * Controls how traffic conditions are factored into routing and travel time estimates.\n *\n * @remarks\n * **Traffic Modes:**\n *\n * - **`live`**: Real-time + historical traffic\n * - Includes current traffic jams and incidents\n * - Considers short-term and long-term road closures\n * - Most accurate for immediate departures\n * - Updates with current conditions\n *\n * - **`historical`**: Typical traffic patterns only\n * - Based on historical data for time of day/week\n * - Ignores current traffic conditions\n * - Good for future trip planning\n * - More predictable for scheduled departures\n *\n * @example\n * ```typescript\n * // Route considering current traffic (departing now)\n * const trafficMode: TrafficInput = 'live';\n *\n * // Route based on typical patterns (planning ahead)\n * const plannedRoute: TrafficInput = 'historical';\n * ```\n *\n * @group Routing\n */\nexport type TrafficInput = 'live' | 'historical';\n\n/**\n * Cost model criteria for route optimization.\n *\n * Defines routing preferences and constraints that influence path selection.\n * Combines route type, traffic consideration, and avoidance criteria to determine\n * what makes a route \"better\" in the eyes of the routing engine.\n *\n * @remarks\n * The cost model affects how the route path is calculated, but does not affect what other data the response includes.\n *\n * The cost model balances multiple factors:\n * - Time efficiency\n * - Distance\n * - Fuel/energy consumption\n * - User preferences (avoid tolls, ferries, etc.)\n * - Traffic conditions\n *\n * @example\n * ```typescript\n * // Fast route avoiding tolls\n * const costModel: CostModel = {\n * routeType: 'fast',\n * traffic: 'live',\n * avoid: ['tollRoads']\n * };\n *\n * // Eco-friendly route avoiding highways\n * const ecoCostModel: CostModel = {\n * routeType: 'efficient',\n * traffic: 'historical',\n * avoid: ['motorways', 'ferries']\n * };\n *\n * // Scenic route for leisure\n * const scenicCostModel: CostModel = {\n * routeType: 'thrilling',\n * avoid: ['motorways', 'tollRoads']\n * };\n * ```\n *\n * @group Routing\n */\nexport type CostModel = {\n /**\n * Specifies something that the route calculation should try to avoid when determining the route.\n * @default None\n */\n avoid?: Avoidable[];\n\n /**\n * Rectangular areas for the routing engine to bypass, each expressed as a {@link HasBBox}.\n *\n * Up to 10 rectangles are supported. Useful for avoiding construction zones,\n * restricted areas, or known congestion points.\n *\n * @remarks\n * Constraints per rectangle:\n * - Maximum size: ~160×160 km\n * - Cannot cross the 180th meridian\n * - Latitude must be between −80° and +80°\n *\n * @example\n * ```typescript\n * // Avoid an area using a BBox array [west, south, east, north]\n * avoidAreas: [\n * [2.265938, 48.81851, 2.41115, 48.90309]\n * ]\n * ```\n */\n avoidAreas?: HasBBox[];\n\n /**\n * Decides how traffic is considered for computing routes.\n *\n * Possible values are:\n * * live: In addition to historical travel times, routing and estimated travel time\n * consider traffic jams and short- and long-term closures during the travel time window.\n * * historical: Routing and estimated travel time consider historical travel times and long term closures.\n * Traffic jams and short-term closures during the travel time window do not influence routing or travel time.\n *\n * @default live\n *\n * @remarks\n * This setting does not affect whether live traffic is included in the response or not.\n * * Considering historical traffic in cost model does not mean live traffic is not included in the response.\n */\n traffic?: TrafficInput;\n\n /**\n * Specifies the type of optimization used when calculating routes.\n * Possible values are:\n *\n * * **fast**: Route calculation is optimized by travel time, while keeping the routes sensible. For example, the calculation may avoid shortcuts along inconvenient side roads or long detours that only save very little time.\n * * **short**: Route calculation is optimized such that a good compromise between small travel time and short travel distance is achieved.\n * * **efficient**: Route calculation is optimized such that a good compromise between small travel time and low fuel or energy consumption is achieved.\n * * **thrilling**: Route calculation is optimized such that routes include interesting or challenging roads and use as few motorways as possible.\n * There is a limit of 900km on routes planned with routeType=thrilling.\n * @default fast\n */\n routeType?: RouteType;\n};\n\ntype DepartArriveOption = 'departAt' | 'arriveBy';\n\n/**\n * Departure or arrival time specification for route planning.\n *\n * Allows specifying either when to depart from the origin or when to arrive at the destination.\n * The routing engine calculates the route optimized for that specific time window, considering\n * traffic patterns for that time of day.\n *\n * @typeParam Option - Whether this specifies departure or arrival time\n *\n * @remarks\n * Traffic conditions vary significantly by:\n * - Time of day (rush hour vs off-peak)\n * - Day of week (weekday vs weekend)\n * - Special events or holidays\n *\n * Specifying a departure or arrival time enables the router to:\n * - Use appropriate traffic data for that time\n * - Plan around rush hour or quiet periods\n * - Calculate accurate arrival/departure times\n * - Account for time-dependent road restrictions\n *\n * **Important Notes:**\n * - Times are processed in the timezone of the origin/destination\n *\n * @example\n * ```typescript\n * // Depart at specific time (morning commute)\n * const departParams: DepartArriveParams = {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * };\n *\n * // Arrive by specific time (catch a flight)\n * const arriveParams: DepartArriveParams = {\n * option: 'arriveBy',\n * date: new Date('2025-10-20T14:00:00Z')\n * };\n *\n * // Plan route avoiding rush hour\n * const offPeakDepart: DepartArriveParams = {\n * option: 'departAt',\n * date: new Date('2025-10-20T10:30:00Z') // After morning rush\n * };\n * ```\n *\n * @group Routing\n */\nexport type DepartArriveParams<Option extends DepartArriveOption = DepartArriveOption> = {\n /**\n * Whether to specify a departure or arrival time.\n *\n * @remarks\n * - `departAt`: Calculate route from this departure time forward\n * - `arriveBy`: Calculate route backward to arrive by this time\n */\n option: Option;\n\n /**\n * The date and time to depart or arrive.\n *\n * @remarks\n * If past dates are supplied or dates that are impossible to achieve\n * (e.g., an imminent arrival date for a very long route), the system\n * will default to departing immediately.\n *\n * Times should be specified in ISO 8601 format or as JavaScript Date objects.\n */\n date: Date;\n};\n\n/**\n * Common parameters shared across all routing service requests.\n *\n * These parameters configure how routes are calculated, including optimization\n * strategy, vehicle characteristics, timing constraints, and travel preferences.\n * They provide a consistent interface across different routing services.\n *\n * @remarks\n * Most routing services (calculateRoute, calculateMatrixRoute, calculateReachableRange)\n * accept these parameters to customize route calculation. They control:\n * - What to optimize for (time, distance, fuel)\n * - Vehicle constraints and capabilities\n * - When to travel (affecting traffic)\n * - What features to avoid\n * - Mode of transportation\n *\n * **Service Compatibility:**\n * - {@link calculateRoute}: All parameters supported\n * - {@link calculateMatrixRoute}: Subset of parameters\n * - {@link calculateReachableRange}: Subset of parameters\n *\n * @example\n * ```typescript\n * // Standard car route avoiding tolls\n * const routingParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'fast',\n * traffic: 'live',\n * avoid: ['tollRoads']\n * },\n * travelMode: 'car',\n * when: {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * }\n * };\n *\n * // Electric vehicle route with consumption model\n * const evRoutingParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'efficient',\n * traffic: 'live'\n * },\n * travelMode: 'car',\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * dimensions: {\n * weightKG: 2000\n * },\n * engine: {\n * consumption: {\n * speedToConsumption: [\n * { speedKMH: 50, consumptionUnitsPer100KM: 15 },\n * { speedKMH: 90, consumptionUnitsPer100KM: 18 },\n * { speedKMH: 120, consumptionUnitsPer100KM: 22 }\n * ]\n * }\n * }\n * },\n * state: {\n * currentChargeInkWh: 60\n * }\n * }\n * };\n *\n * // Truck route with restrictions\n * const truckParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'short',\n * avoid: ['tollRoads', 'ferries']\n * },\n * travelMode: 'truck',\n * vehicle: {\n * model: {\n * dimensions: {\n * weightKG: 40000\n * }\n * },\n * restrictions: {\n * maxSpeedKMH: 90\n * }\n * }\n * };\n * ```\n *\n * @see [Common Routing Parameters Documentation](https://docs.tomtom.com/routing-api/documentation/routing/common-routing-parameters)\n *\n * @group Routing\n */\nexport type CommonRoutingParams = {\n /**\n * Criteria that specifies what paths to prefer during routing.\n */\n costModel?: CostModel;\n\n /**\n * The primary means of transportation to be used while routing.\n *\n * The travel mode for the requested route.\n * Note that the requested travelMode may not be available for the entire route. Where\n * the requested travelMode is not available for a particular section, the element of the\n * response for that section will be 'other'.\n * @default None\n */\n travelMode?: TravelMode;\n\n /**\n * Specifies when to depart or arrive.\n * If past dates are supplied or in a way that are impossible to achieve\n * (e.g. an imminent arrival date for a long route), then it will default to departing now.\n * @default depart now\n */\n when?: DepartArriveParams;\n\n /**\n * Parameters for the vehicle that will be used to drive the route.\n */\n vehicle?: VehicleParameters;\n};\n","/**\n * Whether the vehicle carries an electronic toll collection transponder.\n *\n * Determines which toll roads the routing engine may use: some tolls can only be paid by\n * transponder, others only without one.\n *\n * @group Vehicle\n */\nexport const tollTransponderOptions = ['all', 'unknown', 'none'] as const;\n\n/**\n * Whether the vehicle carries an electronic toll collection transponder.\n *\n * @remarks\n * - `all` — the vehicle has a transponder accepted by every toll operator on the route\n * - `unknown` — transponder availability is unknown, so no assumption is made\n * - `none` — the vehicle has no transponder\n *\n * @example\n * ```typescript\n * const transponder: TollTransponder = 'all';\n * ```\n *\n * @group Vehicle\n */\nexport type TollTransponder = (typeof tollTransponderOptions)[number];\n\n/**\n * Parameters for a vehicle which are related to restrictions applied during route planning.\n */\nexport type VehicleRestrictions = {\n /**\n * Vehicle restrictions applied during route planning.\n */\n restrictions?: {\n /**\n * Maximum speed of the vehicle in kilometers/hour.\n * * Must have a value in the range [0, 250].\n * * A value of 0 means that an appropriate value for the vehicle will be determined and applied during route planning.\n *\n * @default 0\n */\n maxSpeedKMH?: number;\n\n /**\n * Whether the vehicle carries an electronic toll collection transponder.\n *\n * @remarks\n * Affects which toll roads are considered usable: a toll that can only be paid by\n * transponder is avoided when this is `none`, and a toll that cannot be paid by\n * transponder is avoided when this is `all`.\n *\n * @default 'unknown'\n *\n * @example\n * ```typescript\n * restrictions: {\n * tollTransponder: 'all'\n * }\n * ```\n */\n tollTransponder?: TollTransponder;\n };\n};\n","import type { ParseResponseError } from '../shared';\nimport { SDKServiceError } from '../shared';\n\n/**\n * @ignore\n * @param apiError\n * @param serviceName\n */\nexport const parseEVChargingStationsAvailabilityResponseError: ParseResponseError = (apiError, serviceName) => {\n const errorMessage = apiError.data?.detailedError?.message ?? apiError.message;\n return new SDKServiceError(errorMessage, serviceName, apiError.status);\n};\n","import { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { appendCommonParams } from '../shared/request/requestBuildingUtils';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\nconst buildUrlBasePath = (params: ChargingStationsAvailabilityParams): string =>\n resolvePlacesEndpointUrl(params, 'ev/id');\n\n/**\n * Default method for building ev charging stations availability request from {@link ChargingStationsAvailabilityParams}\n * @param params The charging availability parameters, with global configuration already merged into them.\n *\n * @remarks\n * Unlike the other places services, this one keeps `key=` and `apiVersion=` in the query string.\n * `/maps/orbis/places/ev/id` answers 401 to a header-only credential, while `search`,\n * `poiCategories`, `geocode`, `place` and `additionalData` all accept one — probed against\n * production. Moving this one to headers breaks it outright rather than subtly, so it stays until\n * the endpoint catches up.\n */\nexport const buildEVChargingStationsAvailabilityRequest = (params: ChargingStationsAvailabilityParams): URL => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n urlParams.append('id', params.id);\n return url;\n};\n","import type { ChargingStation, ChargingStationsAvailability } from '@tomtom-org/maps-sdk/core';\nimport { toChargingSpeed } from '../shared/ev';\nimport { parseOpeningHours } from '../shared/searchResultParsing';\nimport { toChargingPointAvailability, toConnectorBasedAvailabilities } from './connectorAvailability';\nimport type { ChargingStationsAvailabilityResponseAPI } from './types/apiTypes';\n\nconst toChargingPointStations = (stations: ChargingStation[]) =>\n stations.map((station) => ({\n ...station,\n chargingPoints: station.chargingPoints.map((chargingPoint) => ({\n ...chargingPoint,\n connectors: chargingPoint.connectors?.map((connector) => ({\n ...connector,\n chargingSpeed: toChargingSpeed(connector.ratedPowerKW),\n })),\n })),\n }));\n\n/**\n * Default method for parsing ev charging stations availability from {@link ChargingStationsAvailability}\n * @param apiResponse\n */\nexport const parseEVChargingStationsAvailabilityResponse = (\n apiResponse: ChargingStationsAvailabilityResponseAPI,\n): ChargingStationsAvailability | undefined => {\n const result = apiResponse.results?.[0];\n return result\n ? {\n id: result.id,\n accessType: result.accessType,\n chargingStations: toChargingPointStations(result.chargingStations),\n chargingPointAvailability: toChargingPointAvailability(result.chargingStations),\n connectorAvailabilities: toConnectorBasedAvailabilities(result.chargingStations),\n ...(result.openingHours && { openingHours: parseOpeningHours(result.openingHours) }),\n }\n : undefined;\n};\n","import type { ChargingStationsAvailability } from '@tomtom-org/maps-sdk/core';\nimport type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { evChargingStationsAvailabilityRequestSchema } from './evChargingStationsAvailabilityRequestSchema';\nimport { parseEVChargingStationsAvailabilityResponseError } from './evChargingStationsAvailabilityResponseErrorParser';\nimport { buildEVChargingStationsAvailabilityRequest } from './requestBuilder';\nimport { parseEVChargingStationsAvailabilityResponse } from './responseParser';\nimport type { ChargingStationsAvailabilityResponseAPI } from './types/apiTypes';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\n/**\n * EV Charging Stations Availability service template type.\n * @ignore\n */\nexport type EVChargingStationsAvailabilityTemplate = ServiceTemplate<\n ChargingStationsAvailabilityParams,\n URL,\n ChargingStationsAvailabilityResponseAPI,\n ChargingStationsAvailability | undefined\n>;\n\n/**\n * EV Charging Stations Availability service template main implementation.\n * @ignore\n */\nexport const evChargingStationsAvailabilityTemplate: EVChargingStationsAvailabilityTemplate = {\n requestValidation: { schema: evChargingStationsAvailabilityRequestSchema },\n buildRequest: buildEVChargingStationsAvailabilityRequest,\n sendRequest: get,\n parseResponse: parseEVChargingStationsAvailabilityResponse,\n parseResponseError: parseEVChargingStationsAvailabilityResponseError,\n};\n","import type {\n ChargingPark,\n ChargingParkWithAvailability,\n ChargingStationsAvailability,\n CommonPlaceProps,\n EVChargingStationWithAvailabilityPlaceProps,\n Place,\n Places,\n} from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { evChargingStationsAvailabilityTemplate } from './evChargingStationsAvailabilityTemplate';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\n/**\n * Get real-time availability of electric vehicle charging stations.\n *\n * Provides current operational status of charging points and connectors at EV charging parks,\n * enabling drivers to find available chargers before arriving at a location.\n *\n * @remarks\n * **Access**: needs an API key with EV Charging Stations Availability access, unavailable on a Freemium or\n * Pay As You Grow (PAYG) basis: [request it from TomTom Sales](https://www.tomtom.com/contact-sales/?source_app=docs-portal&source_product=ev-search).\n *\n * Key information returned:\n * - **Point-level status**: Available, Occupied, Reserved, Out of Service\n * - **Connector details**: Power ratings, plug types, current availability\n * - **Aggregated counts**: Quick overview of available vs occupied chargers\n * - **Access information**: Public, private, or restricted access\n * - **Opening hours**: When the charging facility is accessible\n *\n * @param params - Charging availability parameters with station ID\n *\n * @returns Promise resolving to charging station availability information\n *\n * @example\n * ```typescript\n * // Get availability for a specific charging park\n * const availability = await evChargingStationsAvailability({\n * key: 'your-api-key',\n * id: 'charging-park-id-123'\n * });\n *\n * // Check how many chargers are available\n * const availableCount = availability.chargingPointAvailability.statusCounts.Available;\n * console.log(`${availableCount} chargers available`);\n *\n * // Find available CCS connectors\n * const ccsConnectors = availability.connectorAvailabilities.find(\n * ca => ca.connector.type === 'IEC62196Type2CCS'\n * );\n * ```\n *\n * @see [EV Charging Availability API](https://docs.tomtom.com/search-api/documentation)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [EV Charging Stations Availability Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/ev-charging-stations-availability)\n *\n * @group EV Charging\n */\nexport const evChargingStationsAvailability = async (\n params: ChargingStationsAvailabilityParams,\n): Promise<ChargingStationsAvailability | undefined> =>\n callService(params, evChargingStationsAvailabilityTemplate, 'EVChargingStationsAvailability');\n\n/**\n * Enhance a place with real-time EV charging availability data.\n *\n * Fetches availability information for an EV charging station and merges it into\n * the place properties.\n *\n * @remarks\n * Needs the same API key access as {@link evChargingStationsAvailability}. A failed request,\n * including one from a key without that access, is logged and resolves to `undefined`.\n *\n * @param place - The place to enhance with availability data\n * @param commonParams - Common service parameters (e.g. `key`, `customServiceBaseURL`, `onAPIRequest`) forwarded to the availability request\n *\n * @returns Promise resolving to the place with merged availability information\n * @returns undefined if no availability data is present for the `place` input parameter.\n *\n * @example\n * ```typescript\n * // After search, enhance place with availability\n * const searchResult = await discoverPlaces({ query: 'EV charging', ... });\n * const place = searchResult.features[0];\n *\n * const enhancedPlace = await getPlaceWithEVAvailability(place);\n * const availability = enhancedPlace.properties.chargingPark?.availability;\n *\n * if (availability) {\n * console.log('Available chargers:', availability.chargingPointAvailability.count);\n * }\n * ```\n *\n * @group EV Charging\n */\nexport const getPlaceWithEVAvailability = async <P extends CommonPlaceProps = CommonPlaceProps>(\n place: Place<P>,\n commonParams?: Omit<ChargingStationsAvailabilityParams, 'id'>,\n): Promise<Place<EVChargingStationWithAvailabilityPlaceProps> | undefined> => {\n const availabilityId = place.properties.dataSources?.chargingAvailability?.id;\n if (!availabilityId) {\n return undefined;\n }\n try {\n const availability = await evChargingStationsAvailability({ ...commonParams, id: availabilityId });\n const poi = place.properties.poi;\n return availability\n ? {\n ...place,\n properties: {\n ...place.properties,\n // We override poi opening hours with the ones from the EV call, which might be better supported:\n ...(poi && { poi: { ...poi, openingHours: availability.openingHours } }),\n chargingPark: {\n ...(place.properties.chargingPark as ChargingPark),\n availability,\n },\n },\n }\n : undefined;\n } catch (e) {\n // (Likely a QPS limit error)\n console.error(e);\n return undefined;\n }\n};\n\n/**\n * Enhance multiple places with real-time EV charging availability data.\n *\n * Fetches availability information for all EV charging stations in a collection\n * and merges it into their properties. Non-EV places and EV stations without availability data are returned unchanged.\n *\n * @remarks\n * **Important**: Availability requests are made sequentially to avoid exceeding\n * API rate limits (QPS - Queries Per Second). For large result sets, this may\n * take some time.\n *\n * Needs the same API key access as {@link evChargingStationsAvailability}. A place whose request\n * fails, including for a key without that access, counts as having unknown availability.\n *\n * @param places - Collection of places to enhance\n * @param options - Configuration options, including common service parameters (e.g. `key`, `customServiceBaseURL`, `onAPIRequest`) forwarded to each availability request\n *\n * @returns Promise resolving to places collection with merged availability\n *\n * @example\n * ```typescript\n * // Search for charging stations and add availability\n * const results = await discoverPlaces({\n * query: 'EV charging',\n * at: [4.9, 52.3],\n * radius: 5000\n * });\n *\n * const withAvailability = await getPlacesWithEVAvailability(results, {\n * excludeIfAvailabilityUnknown: true // Filter out stations with unknown availability\n * });\n *\n * // Display only stations with known availability\n * withAvailability.features.forEach(place => {\n * const available = place.properties.chargingPark?.availability?.chargingPointAvailability.count;\n * console.log(`${place.properties.poi?.name}: ${available} chargers`);\n * });\n * ```\n *\n * @group EV Charging\n */\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n excludeIfAvailabilityUnknown: true;\n },\n): Promise<Places<EVChargingStationWithAvailabilityPlaceProps>>;\n\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n /**\n * If true, places with unknown availability will be filtered out. Otherwise, they will be included.\n * @default false\n */\n excludeIfAvailabilityUnknown?: false;\n },\n): Promise<Places<P | EVChargingStationWithAvailabilityPlaceProps>>;\n\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n excludeIfAvailabilityUnknown?: boolean;\n },\n): Promise<Places<P | EVChargingStationWithAvailabilityPlaceProps>> {\n const { excludeIfAvailabilityUnknown, ...commonParams } = options ?? {};\n const enhancedPlaces: Array<Place<P> | Place<EVChargingStationWithAvailabilityPlaceProps>> = [];\n for (const place of places.features) {\n // (We fetch the availabilities sequentially on purpose to prevent QPS limit errors)\n const placeWithAvailability = await getPlaceWithEVAvailability(place, commonParams);\n if (placeWithAvailability) {\n enhancedPlaces.push(placeWithAvailability);\n } else if (!excludeIfAvailabilityUnknown) {\n enhancedPlaces.push(place);\n }\n }\n return { ...places, features: enhancedPlaces, bbox: bboxFromGeoJSON(enhancedPlaces) };\n}\n\n/**\n * Type guard to check if a ChargingPark has availability data.\n *\n * @param chargingPark - The charging park to check\n * @returns True if the charging park has availability data\n *\n * @group EV Charging\n */\nexport const hasChargingAvailability = (\n chargingPark: ChargingPark | ChargingParkWithAvailability | undefined,\n): chargingPark is ChargingParkWithAvailability =>\n Boolean(chargingPark && 'availability' in chargingPark && chargingPark.availability);\n","/**\n * The single-result form of a places service: takes a bare query or the params bar `limit`, and\n * returns the first feature or throws.\n *\n * @ignore\n */\nexport const fetchFirstPlace = async <Params extends { query?: string }, Feature>(\n functionName: string,\n queryOrParams: string | Params,\n service: (params: (Params | { query: string }) & { limit: 1 }) => Promise<{ features: Feature[] }>,\n): Promise<Feature> => {\n const params = typeof queryOrParams === 'string' ? { query: queryOrParams } : queryOrParams;\n const [first] = (await service({ ...params, limit: 1 })).features;\n if (!first) throw new Error(`${functionName}: no results found for \"${params.query}\"`);\n return first;\n};\n","import { z } from 'zod';\nimport { hasBBoxSchema, hasLngLatSchema } from './geometriesSchema';\n\n/** The point arm on its own, for the endpoints that bias by point only. @ignore */\nexport const pointGeoBiasSchema = z.object({\n position: hasLngLatSchema,\n radiusMeters: z.number().optional(),\n});\n\n/** @ignore */\nexport const geoBiasSchema = z.union([pointGeoBiasSchema, z.object({ boundingBox: hasBBoxSchema })]);\n","import { z } from 'zod';\nimport { geoBiasSchema } from './geoBiasSchema';\n\n/**\n * @ignore\n */\nexport const commonGeocodeAndFuzzySearchParamsSchema = z.object({\n typeahead: z.boolean().optional(),\n offset: z.number().max(1900).optional(),\n geoBias: geoBiasSchema.optional(),\n countries: z.array(z.string()).optional(),\n});\n","import { z } from 'zod';\nimport { commonGeocodeAndFuzzySearchParamsSchema } from '../shared/schema/commonGeocodeAndFuzzySearchParamsSchema';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { commonPlacesParamsSchema } from '../shared/schema/commonPlacesParamsSchema';\n\n/**\n * @ignore\n */\nexport const geocodingRequestSchema = commonServiceRequestSchema\n .extend(commonPlacesParamsSchema.shape)\n .extend(commonGeocodeAndFuzzySearchParamsSchema.shape)\n .extend({ query: z.string() });\n","import { isNil } from 'lodash-es';\nimport type { GetObject } from '../shared';\nimport { arrayToCSV } from '../shared/arrays';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport {\n appendGeoBiasParams,\n appendPlacesLanguageParam,\n buildPlacesRequestHeaders,\n} from '../shared/request/requestBuildingUtils';\nimport type { GeocodingParams } from './types/geocodingParams';\n\nconst buildUrlBasePath = (params: GeocodingParams): string => resolvePlacesEndpointUrl(params, 'geocode');\n\n/**\n * Default method for building geocoding request from {@link GeocodingParams}\n * @param params The geocoding parameters, with global configuration already merged into them.\n */\nexport const buildGeocodingRequest = (params: GeocodingParams): GetObject => {\n const url = new URL(`${buildUrlBasePath(params)}/${encodeURIComponent(params.query)}.json`);\n const urlParams = url.searchParams;\n appendPlacesLanguageParam(urlParams, params);\n // geocoding specific parameters:\n params.typeahead && urlParams.append('typeahead', String(params.typeahead));\n !isNil(params.limit) && urlParams.append('limit', String(params.limit));\n !isNil(params.offset) && urlParams.append('ofs', String(params.offset));\n appendGeoBiasParams(urlParams, params.geoBias);\n params.countries && urlParams.append('countrySet', arrayToCSV(params.countries));\n params.extendedPostalCodesFor &&\n urlParams.append('extendedPostalCodesFor', arrayToCSV(params.extendedPostalCodesFor));\n params.mapcodes && urlParams.append('mapcodes', arrayToCSV(params.mapcodes));\n params.view && urlParams.append('view', params.view);\n params.geographyTypes && urlParams.append('entityTypeSet', arrayToCSV(params.geographyTypes));\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import type { GeographyType, Place } from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON, bboxOnlyIfWithArea, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { omit } from 'lodash-es';\nimport { apiToGeoJSONBBox, latLonAPIToPosition } from '../shared/geometry';\nimport type { GeocodingResponseAPI, GeocodingResultAPI } from './types/apiTypes';\nimport type { GeocodingProps, GeocodingResponse } from './types/geocodingResponse';\n\nconst parseApiResult = (result: GeocodingResultAPI): Place<GeocodingProps> => {\n const { position, boundingBox, dist, entryPoints, addressRanges, entityType, id, ...rest } = result;\n\n return {\n type: 'Feature',\n geometry: toPointGeometry(latLonAPIToPosition(position)),\n ...(boundingBox && { bbox: apiToGeoJSONBBox(boundingBox) }),\n id,\n properties: {\n ...omit(rest, 'viewport'),\n ...(dist && { distance: dist }),\n ...(entityType && { geographyType: entityType.split(',') as GeographyType[] }),\n ...(entryPoints && {\n entryPoints: entryPoints.map((entrypoint) => ({\n ...entrypoint,\n position: latLonAPIToPosition(entrypoint.position),\n })),\n }),\n ...(addressRanges && {\n addressRanges: {\n ...addressRanges,\n from: latLonAPIToPosition(addressRanges.from),\n to: latLonAPIToPosition(addressRanges.to),\n },\n }),\n },\n };\n};\n\n/**\n * Default method for parsing geocoding request from {@link GeocodingResponse}\n * @param apiResponse\n */\nexport const parseGeocodingResponse = (apiResponse: GeocodingResponseAPI): GeocodingResponse => {\n const results = apiResponse.results;\n const features = results.map(parseApiResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { geocodingRequestSchema } from './geocodingRequestSchema';\nimport { buildGeocodingRequest } from './requestBuilder';\nimport { parseGeocodingResponse } from './responseParser';\nimport type { GeocodingResponseAPI } from './types/apiTypes';\nimport type { GeocodingParams } from './types/geocodingParams';\nimport type { GeocodingResponse } from './types/geocodingResponse';\n\n/**\n * Geocoding service template type.\n * @ignore\n */\nexport type GeocodingTemplate = ServiceTemplate<GeocodingParams, GetObject, GeocodingResponseAPI, GeocodingResponse>;\n\n/**\n * Geocoding service template main implementation.\n * @ignore\n */\nexport const geocodingTemplate: GeocodingTemplate = {\n requestValidation: { schema: geocodingRequestSchema },\n buildRequest: buildGeocodingRequest,\n sendRequest: get,\n parseResponse: parseGeocodingResponse,\n};\n","import type { Place } from '@tomtom-org/maps-sdk/core';\nimport { fetchFirstPlace } from '../shared/firstPlace';\nimport { callService } from '../shared/serviceTemplate';\nimport { geocodingTemplate } from './geocodingTemplate';\nimport type { GeocodingParams } from './types/geocodingParams';\nimport type { GeocodingProps, GeocodingResponse } from './types/geocodingResponse';\n\n/**\n * Convert addresses into geographic coordinates (geocoding).\n *\n * The Geocode service translates addresses and place names into geographic coordinates,\n * enabling you to position markers on maps, calculate routes, or perform spatial analysis.\n *\n * @remarks\n * This service is optimized for address lookup and does not return POIs (Points of Interest).\n * For POI search, use the {@link discoverPlaces} function instead.\n *\n * Features:\n * - Highly tolerant of typos and incomplete addresses\n * - Handles various address formats and components\n * - Supports street addresses, intersections, and cross streets\n * - Works with higher-level geographies (cities, counties, states, countries)\n * - Returns structured address components\n *\n * @param params - Geocoding parameters including the address query\n *\n * @returns Promise resolving to geocoded location results\n *\n * @example\n * ```typescript\n * // Geocode a complete address\n * const result = await geocode({\n * key: 'your-api-key',\n * query: '1600 Pennsylvania Avenue NW, Washington, DC'\n * });\n *\n * // Geocode with partial address\n * const partialResult = await geocode({\n * key: 'your-api-key',\n * query: 'Amsterdam, Netherlands'\n * });\n *\n * // Geocode with bias towards specific location\n * const biasedResult = await geocode({\n * key: 'your-api-key',\n * query: 'Main Street',\n * at: [4.9041, 52.3676], // Bias toward Amsterdam\n * limit: 5\n * });\n *\n * // Geocode an intersection\n * const intersection = await geocode({\n * key: 'your-api-key',\n * query: '5th Avenue & 42nd Street, New York'\n * });\n * ```\n *\n * @see [Geocode API Documentation](https://docs.tomtom.com/search-api/documentation/geocoding-service/geocode)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Geocoding Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/geocoding)\n *\n * @group Geocoding\n */\nexport const geocode = async (params: GeocodingParams): Promise<GeocodingResponse> =>\n callService(params, geocodingTemplate, 'Geocode');\n\n/**\n * Geocode a query and return the single best-matching place.\n *\n * Convenience wrapper around {@link geocode} that returns the top result directly.\n *\n * @param queryOrParams - Address or place name to geocode, or the full {@link GeocodingParams} (bar `limit`)\n * @returns Promise resolving to the best-matching place\n * @throws {Error} If no results are found for the query\n *\n * @remarks\n * Use {@link geocode} with a `limit` parameter when you need to handle the case\n * where no results are found without throwing.\n *\n * @example\n * ```typescript\n * const place = await geocodeOne('Amsterdam Centraal');\n * console.log(place.geometry.coordinates); // [lng, lat]\n *\n * const biased = await geocodeOne({ query: 'Main Street', geoBias: { position: [4.9041, 52.3676] }, signal });\n * ```\n *\n * @group Geocoding\n */\nexport const geocodeOne = async (\n queryOrParams: string | Omit<GeocodingParams, 'limit'>,\n): Promise<Place<GeocodingProps>> => fetchFirstPlace('geocodeOne', queryOrParams, geocode);\n","import { z } from 'zod';\nimport { featureCollectionSchema, featureSchema } from '../shared/schema/geometriesSchema';\n\nconst geometryDataRequestMandatory = z.object({\n geometries: z.union([\n featureCollectionSchema,\n z\n .array(z.union([z.string(), featureSchema]))\n .min(1)\n .max(20),\n ]),\n});\n\nconst geometryDataRequestOptional = z.object({\n zoom: z.number().min(0).max(22).optional(),\n});\n\nexport const geometryDataRequestSchema = geometryDataRequestMandatory.extend(geometryDataRequestOptional.shape);\n","import type { Place, Places } from '@tomtom-org/maps-sdk/core';\nimport type { GetObject } from '../shared';\nimport { arrayToCSV } from '../shared/arrays';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { appendOptionalParam, buildPlacesRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { GeometriesInput, GeometryParams } from './types/geometryDataParams';\n\nconst buildUrlBasePath = (params: GeometryParams): string => resolvePlacesEndpointUrl(params, 'additionalData.json');\n\nconst getGeometryIDs = (placesArray: Place[]): string[] =>\n placesArray.map((place) => place.properties.dataSources?.geometry?.id as string).filter((id) => id);\n\n// (@see geometryDataRequestSchema)\nconst appendGeometries = (urlParams: URLSearchParams, geometries: GeometriesInput | Places | Place[]): void => {\n let geometryIDs: string[];\n\n if (Array.isArray(geometries)) {\n // (assuming min and max length already validated)\n if (typeof geometries[0] === 'string') {\n geometryIDs = geometries as string[];\n } else {\n geometryIDs = getGeometryIDs(geometries as Place[]);\n }\n } else {\n // (assuming already validated as FeatureCollection)\n geometryIDs = getGeometryIDs(geometries.features);\n }\n\n urlParams.append('geometries', arrayToCSV(geometryIDs));\n};\n\n/**\n * Default function for building a geometry data request from {@link GeometryDataParams}\n * @param params The geometry data parameters, with global configuration already merged into them.\n */\nexport const buildGeometryDataRequest = (params: GeometryParams): GetObject => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n // (no language in this service, and the credentials travel as headers)\n appendGeometries(urlParams, params.geometries);\n appendOptionalParam(urlParams, 'geometriesZoom', params.zoom);\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import { bboxFromGeoJSON, generateId, PolygonFeature, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { GeometryDataResponseAPI } from './types/apiTypes';\n\n/**\n * Default geometry data API response parsing.\n * * The API response consists of an array, with a FeatureCollection (with only one feature) for each geometry.\n * * The parsed response consists of a fully-GeoJSON-compatible FeatureCollection with a Feature for each geometry.\n * * Each geometry ID is included in each GeoJSON feature \"id\" field.\n * @param apiResponse\n */\nexport const parseGeometryDataResponse = (apiResponse: GeometryDataResponseAPI): PolygonFeatures => {\n const features = apiResponse.additionalData\n .flatMap((data) =>\n (data.geometryData as PolygonFeatures)?.features.map((feature) => ({\n ...feature,\n id: feature.id ?? generateId(),\n bbox: bboxFromGeoJSON(feature.geometry),\n })),\n )\n .filter((feature) => feature) as PolygonFeature[];\n return {\n type: 'FeatureCollection',\n bbox: bboxFromGeoJSON(features),\n features,\n };\n};\n","import type { PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { geometryDataRequestSchema } from './geometryDataRequestSchema';\nimport { buildGeometryDataRequest } from './requestBuilder';\nimport { parseGeometryDataResponse } from './responseParser';\nimport type { GeometryDataResponseAPI } from './types/apiTypes';\nimport type { GeometryParams } from './types/geometryDataParams';\n\n/**\n * @ignore\n */\nexport type GeometryDataTemplate = ServiceTemplate<GeometryParams, GetObject, GeometryDataResponseAPI, PolygonFeatures>;\n\n/**\n * @ignore\n */\nexport const geometryDataTemplate: GeometryDataTemplate = {\n requestValidation: { schema: geometryDataRequestSchema },\n buildRequest: buildGeometryDataRequest,\n sendRequest: get,\n parseResponse: parseGeometryDataResponse,\n};\n","import type { CommonPlaceProps, Places, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { geometryDataTemplate } from './geometryDataTemplate';\nimport type { GeometryDataParams, GeometryParams, GeometryPlaceParams } from './types/geometryDataParams';\n\n/**\n * Merge our internal Places \"properties\" response with Geometry data\n * @param places\n * @param geometries\n * @returns FeatureCollection<Polygon | MultiPolygon>,\n */\nconst mergePlacesWithGeometries = (places: Places, geometries: PolygonFeatures): PolygonFeatures<CommonPlaceProps> => {\n const placesIdMap = places.features.reduce(\n (acc, place) => {\n const geometryId = place.properties.dataSources?.geometry?.id;\n\n if (geometryId) {\n acc[geometryId] = {\n ...place.properties,\n placeCoordinates: place.geometry.coordinates,\n };\n }\n return acc;\n },\n {} as Record<string, unknown>,\n );\n\n const features = geometries.features.map((feature) => {\n if (feature.id && placesIdMap[feature.id]) {\n return { ...feature, properties: placesIdMap[feature.id] };\n }\n\n return feature;\n });\n\n return {\n type: 'FeatureCollection',\n bbox: geometries.bbox,\n features,\n } as PolygonFeatures<CommonPlaceProps>;\n};\n\n/**\n * Retrieve polygon geometries representing geographic area boundaries.\n *\n * The Geometry Data service returns coordinate sets that define the outlines of\n * geographic areas such as cities, countries, administrative regions, or POI footprints.\n * These polygons enable visualization of area boundaries, spatial analysis, and\n * geofencing applications.\n *\n * @remarks\n * Key features:\n * - **Batch requests**: Fetch up to 20 geometries in a single call\n * - **Multiple scales**: From countries down to building footprints\n * - **Place integration**: Can merge with place data for enriched results\n * - **Standard GeoJSON**: Returns standard Polygon/MultiPolygon features\n *\n * Common use cases:\n * - Display city or country boundaries on maps\n * - Show POI building footprints\n * - Create geofences for spatial queries\n * - Visualize administrative divisions\n * - Calculate areas and spatial relationships\n *\n * @param params - Geometry parameters with IDs or places to fetch boundaries for\n *\n * @returns Promise resolving to polygon features representing area boundaries\n *\n * @example\n * ```typescript\n * // Fetch geometry by ID\n * const cityBoundary = await geometryData({\n * key: 'your-api-key',\n * geometries: ['geometry-id-123']\n * });\n *\n * // Fetch multiple geometries at once\n * const boundaries = await geometryData({\n * key: 'your-api-key',\n * geometries: ['country-id-1', 'city-id-2', 'poi-id-3']\n * });\n *\n * // Fetch and merge with place data\n * const searchResults = await discoverPlaces({ query: 'Amsterdam' });\n * const withBoundaries = await geometryData({\n * key: 'your-api-key',\n * geometries: searchResults // Places with geometry IDs\n * });\n * // Result includes both place properties and polygon boundaries\n * ```\n *\n * @see [Geometry Data API Documentation](https://docs.tomtom.com/search-api/documentation/additional-data-service/additional-data)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Geometry Data Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/geometry-data)\n *\n * @group Geometry\n */\nexport async function geometryData(params: GeometryDataParams): Promise<PolygonFeatures>;\nexport async function geometryData(params: GeometryPlaceParams): Promise<PolygonFeatures<CommonPlaceProps>>;\nexport async function geometryData(params: GeometryParams) {\n const geometryResult = await callService(params, geometryDataTemplate, 'GeometryData');\n\n // If params.geometries is a FeatureCollection(Place), the properties will be merged with geometry results.\n if (!Array.isArray(params.geometries) && params.geometries.type === 'FeatureCollection') {\n return mergePlacesWithGeometries(params.geometries, geometryResult);\n }\n\n return geometryResult;\n}\n","import { views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\n\nconst placeDetailsRequestMandatory = z.object({\n entityId: z.string(),\n});\n\nconst placeDetailsRequestOptional = z.object({\n mapcodes: z.array(z.string()).optional(),\n view: z.enum(views).optional(),\n openingHours: z.string().optional(),\n timeZone: z.string().optional(),\n relatedPois: z.string().optional(),\n});\n\n/**\n * @ignore\n */\nexport const placeDetailsRequestSchema = placeDetailsRequestMandatory.extend(placeDetailsRequestOptional.shape);\n","import type { CommonServiceParams, GetObject } from '../shared';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport {\n appendByJoiningParamValue,\n appendOptionalParam,\n appendPlacesLanguageParam,\n buildPlacesRequestHeaders,\n} from '../shared/request/requestBuildingUtils';\nimport type { PlaceDetailsParams } from './types';\n\nconst buildUrlBasePath = (params: CommonServiceParams): string => resolvePlacesEndpointUrl(params, 'place.json');\n\n/**\n * Default method for building place by id request from {@link PlaceDetailsParams}\n * @param params The place by id parameters, with global configuration already merged into them.\n */\nexport const buildPlaceDetailsRequest = (params: PlaceDetailsParams): GetObject => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n const urlParams = url.searchParams;\n appendPlacesLanguageParam(urlParams, params);\n appendOptionalParam(urlParams, 'entityId', params.entityId);\n appendByJoiningParamValue(urlParams, 'mapcodes', params.mapcodes);\n appendOptionalParam(urlParams, 'view', params.view);\n appendOptionalParam(urlParams, 'openingHours', params.openingHours);\n appendOptionalParam(urlParams, 'timeZone', params.timeZone);\n appendOptionalParam(urlParams, 'relatedPois', params.relatedPois);\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import { parseSearchAPIResult } from '../shared/searchResultParsing';\nimport type { PlaceDetailsResponse, PlaceDetailsResponseAPI } from './types';\n\n/**\n * Default method for parsing place by id response.\n * @param apiResponse The place by id response.\n */\nexport const parsePlaceDetailsResponse = (apiResponse: PlaceDetailsResponseAPI): PlaceDetailsResponse =>\n apiResponse.results?.length ? parseSearchAPIResult(apiResponse.results[0]) : undefined;\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { placeDetailsRequestSchema } from './placeDetailsSchema';\nimport { buildPlaceDetailsRequest } from './requestBuilder';\nimport { parsePlaceDetailsResponse } from './responseParser';\nimport type { PlaceDetailsParams, PlaceDetailsResponse, PlaceDetailsResponseAPI } from './types';\n\n/**\n * Place details template type.\n * @ignore\n */\nexport type PlaceDetailsTemplate = ServiceTemplate<\n PlaceDetailsParams,\n GetObject,\n PlaceDetailsResponseAPI,\n PlaceDetailsResponse\n>;\n\n/**\n * Place details template main implementation.\n * @ignore\n */\nexport const placeDetailsTemplate: PlaceDetailsTemplate = {\n requestValidation: { schema: placeDetailsRequestSchema },\n buildRequest: buildPlaceDetailsRequest,\n sendRequest: get,\n parseResponse: parsePlaceDetailsResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport { placeDetailsTemplate } from './placeDetailsTemplate';\nimport type { PlaceDetailsParams, PlaceDetailsResponse } from './types';\n\n/**\n * Retrieve detailed information about a place using its unique identifier.\n *\n * The place details service fetches comprehensive data for a specific place when you\n * have its ID from a previous search or from a place's dataSources. This is useful\n * for getting additional details or refreshing information about a known location.\n *\n * @remarks\n * Use cases:\n * - **Fetch POI details**: Get extended information like reviews, photos, amenities\n * - **Refresh place data**: Update information for a cached place\n * - **Deep linking**: Allow users to share/bookmark specific places\n * - **Related POI navigation**: Explore parent/child relationships\n *\n * The ID can be obtained from:\n * - Previous search results (place.id)\n * - POI details data source (place.properties.dataSources.poiDetails.id)\n * - Related POIs (place.properties.relatedPois[].id)\n * - Deep links or bookmarks\n *\n * @param params - Place details parameters with the place identifier\n *\n * @returns Promise resolving to detailed place information\n *\n * @example\n * ```typescript\n * // Get a search result's full details\n * const searchResult = await discoverPlaces({ query: 'Eiffel Tower' });\n * const placeId = searchResult.features[0].id;\n *\n * const placeDetails = await getPlaceDetails({\n * key: 'your-api-key',\n * entityId: placeId\n * });\n *\n * // Get extended POI details\n * const place = searchResult.features[0];\n * const poiDetailsId = place.properties.dataSources?.poiDetails?.id;\n *\n * if (poiDetailsId) {\n * const detailedPOI = await getPlaceDetails({\n * key: 'your-api-key',\n * entityId: poiDetailsId\n * });\n * // May include additional photos, reviews, extended hours, etc.\n * }\n *\n * // Navigate to related POI\n * const relatedPOI = place.properties.relatedPois?.[0];\n * if (relatedPOI) {\n * const parentPlace = await getPlaceDetails({\n * key: 'your-api-key',\n * entityId: relatedPOI.id\n * });\n * console.log('Parent location:', parentPlace.properties.address);\n * }\n * ```\n *\n * @see [Place by ID API Documentation](https://docs.tomtom.com/search-api/documentation/place-by-id-service/place-by-id)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n *\n * @group Places Search\n */\nexport const getPlaceDetails = async (params: PlaceDetailsParams): Promise<PlaceDetailsResponse> =>\n callService(params, placeDetailsTemplate, 'PlaceDetails');\n","import type { Language } from '@tomtom-org/maps-sdk/core';\nimport type { POICategoryResult } from './types';\n\nconst cache = new Map<Language, POICategoryResult[]>();\nconst textCache = new Map<Language, Record<string, POICategoryResult>>();\n\nconst DEFAULT_LANGUAGE: Language = 'en-GB';\n\nconst getWithDefault = (language: Language | undefined): Language => language ?? DEFAULT_LANGUAGE;\n\nexport const normalizeText = (text: string): string => text.toLowerCase().replaceAll(/[\\s\\-_,]+/g, '');\n\n/** @ignore */\nexport const getCachedCategories = (language: Language | undefined): POICategoryResult[] | undefined => {\n return cache.get(getWithDefault(language));\n};\n\n/** @ignore */\nexport const setCachedCategories = (language: Language | undefined, categories: POICategoryResult[]): void => {\n const lang = getWithDefault(language);\n cache.set(lang, categories);\n textCache.set(lang, buildTextCache(categories));\n};\n\n/** @ignore */\nexport const buildTextCache = (categories: POICategoryResult[]): Record<string, POICategoryResult> => {\n const cache: Record<string, POICategoryResult> = {};\n for (const category of categories) {\n const texts = [category.name, ...category.synonyms].map(normalizeText);\n for (const text of texts) {\n cache[text] = category;\n }\n }\n return cache;\n};\n\n/** @ignore */\nexport const getCachedTextEntries = (language: Language | undefined): Record<string, POICategoryResult> =>\n textCache.get(language ?? DEFAULT_LANGUAGE) ?? {};\n","import { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\n\n/**\n * @ignore\n */\nexport const poiCategoriesRequestSchema = commonServiceRequestSchema.extend({\n filters: z.array(z.string()).optional(),\n});\n","import type { GetObject } from '../shared';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { appendPlacesLanguageParam, buildPlacesRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { POICategoriesParams } from './types';\n\n/**\n * Builds the POI categories request URL from the merged params.\n * The `filters` param is intentionally omitted — it is applied client-side.\n * @ignore\n */\nexport const buildPoiCategoriesRequest = (params: POICategoriesParams): GetObject => {\n const baseUrl = resolvePlacesEndpointUrl(params, 'poiCategories.json');\n const url = new URL(baseUrl);\n appendPlacesLanguageParam(url.searchParams, params);\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import { type POICategory, poiIDsToCategories } from '@tomtom-org/maps-sdk/core';\nimport type { POICategoriesResponse, POICategoryResult, PoiCategoriesResponseAPI } from './types';\n\nconst parsePoiCategory = (api: PoiCategoriesResponseAPI['poiCategories'][number]): POICategoryResult | null => {\n const code = poiIDsToCategories[api.id];\n if (!code) {\n console.error(\n `Received unknown POI category ID ${api.id} (${api.name}). This category will be skipped and unavailable for filtering.`,\n );\n return null;\n }\n return {\n code,\n name: api.name,\n childCategoryCodes: api.childCategoryIds\n .map((id) => poiIDsToCategories[id])\n .filter((c): c is POICategory => !!c),\n synonyms: api.synonyms,\n };\n};\n\n/**\n * @ignore\n */\nexport const parsePoiCategoriesResponse = (apiResponse: PoiCategoriesResponseAPI): POICategoriesResponse => ({\n poiCategories: apiResponse.poiCategories.map(parsePoiCategory).filter((c) => c !== null),\n});\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { poiCategoriesRequestSchema } from './poiCategoriesRequestSchema';\nimport { buildPoiCategoriesRequest } from './requestBuilder';\nimport { parsePoiCategoriesResponse } from './responseParser';\nimport type { POICategoriesParams, POICategoriesResponse, PoiCategoriesResponseAPI } from './types';\n\n/**\n * @ignore\n */\nexport type PoiCategoriesTemplate = ServiceTemplate<\n POICategoriesParams,\n GetObject,\n PoiCategoriesResponseAPI,\n POICategoriesResponse\n>;\n\n/**\n * @ignore\n */\nexport const poiCategoriesTemplate: PoiCategoriesTemplate = {\n requestValidation: { schema: poiCategoriesRequestSchema },\n buildRequest: buildPoiCategoriesRequest,\n getAPIVersion: () => 1,\n sendRequest: get,\n parseResponse: parsePoiCategoriesResponse,\n};\n","import type { Language, POICategory } from '@tomtom-org/maps-sdk/core';\nimport { mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { getCachedCategories, getCachedTextEntries, normalizeText, setCachedCategories } from './cache';\nimport { poiCategoriesTemplate } from './poiCategoriesTemplate';\nimport type { POICategoriesParams, POICategoriesResponse, POICategoryResult } from './types';\n\n/**\n * Filters cached categories for `language` against `filters` keyword strings.\n * Each filter is normalized and matched as a substring of category names and synonyms.\n * Results are merged, deduplicated by code, and pruned of child categories whose parent also matched.\n *\n * @param language - Language key for the cache lookup.\n * @param filters - Keyword strings to match against names and synonyms.\n */\nconst filterCategories = (language: Language | undefined, filters: string[]): POICategoryResult[] => {\n const cachedTextEntries = getCachedTextEntries(language);\n const resultsByCode = new Map<POICategory, POICategoryResult>();\n for (const filterTerm of filters) {\n const normalizedFilterInput = normalizeText(filterTerm);\n for (const [text, category] of Object.entries(cachedTextEntries)) {\n // if the category text includes the filter input, add the category to the results:\n if (text.includes(normalizedFilterInput) && category) {\n resultsByCode.set(category.code, category);\n }\n }\n }\n\n // Remove child categories whose parent also matched — the parent already implies them.\n for (const category of resultsByCode.values()) {\n for (const childCode of category.childCategoryCodes) {\n resultsByCode.delete(childCode);\n }\n }\n return [...resultsByCode.values()];\n};\n\n/**\n * Retrieve the list of POI category types supported by the TomTom Search API.\n *\n * The returned `code` values (`POICategory`) serve two purposes:\n * - Pass to `discoverPlaces({ poiCategories })` to filter search results by category.\n * - Pass to `POIsModule.filterCategories()` to filter the map's built-in POI icons.\n * Generic parent codes (e.g. `'RESTAURANT'`) are supported there; overly specific child\n * codes (e.g. `'ITALIAN_RESTAURANT'`) are not — use a parent or a `POICategoryGroup` instead.\n *\n * Results are cached in memory per language. The first call fetches from the API;\n * all subsequent calls — including filtered ones — are served from the cache.\n *\n * @param params - Optional parameters. Uses global `TomTomConfig` by default.\n *\n * @example\n * ```typescript\n * // All categories\n * const { poiCategories: all } = await getPOICategories();\n *\n * // Filtered by keyword, merged and deduplicated\n * const { poiCategories: gyms } = await getPOICategories({ filters: ['gym'] });\n * gyms.forEach(c => console.log(c.code, c.name, c.childCategoryCodes));\n * ```\n *\n * @group POI Categories\n */\nexport const getPOICategories = async (params: POICategoriesParams = {}): Promise<POICategoriesResponse> => {\n const { language } = mergeFromGlobal(params);\n\n let allCategories = getCachedCategories(language);\n\n if (!allCategories) {\n const response = await callService(params, poiCategoriesTemplate, 'POICategories');\n allCategories = response.poiCategories;\n setCachedCategories(language, allCategories);\n }\n\n if (params.filters?.length) {\n return { poiCategories: filterCategories(language, params.filters) };\n }\n\n return { poiCategories: allCategories };\n};\n\n/**\n * Convenience wrapper around {@link getPOICategories} that returns a flat array of `POICategory` codes.\n *\n * The returned codes serve two purposes:\n * - Pass to `discoverPlaces({ poiCategories })` to filter search results by category.\n * - Pass to `POIsModule.filterCategories()` to filter the map's built-in POI icons.\n * Generic parent codes (e.g. `'RESTAURANT'`) are supported there; overly specific child\n * codes (e.g. `'ITALIAN_RESTAURANT'`) are not — use a parent or a `POICategoryGroup` instead.\n *\n * Accepts the same parameters as {@link getPOICategories} — use `filters` to narrow by keyword.\n *\n * @param params - Optional parameters. Uses global `TomTomConfig` by default.\n *\n * @example\n * ```typescript\n * // All category codes\n * const codes = await getPOICategoryCodes();\n *\n * // Codes matching 'restaurant' — for search or generic map POI filtering\n * const restaurantCodes = await getPOICategoryCodes({ filters: ['restaurant'] });\n * ```\n *\n * @group POI Categories\n */\nexport const getPOICategoryCodes = async (params: POICategoriesParams = {}): Promise<POICategory[]> => {\n const { poiCategories: categories } = await getPOICategories(params);\n return categories.map((category) => category.code);\n};\n","import type { ParseResponseError } from '../shared';\nimport { SDKServiceError } from '../shared';\nimport type { RoutingAPIResponseError } from '../shared/types/apiResponseErrorTypes';\n\n/**\n * @ignore\n * @param apiError\n * @param serviceName\n */\nexport const parseRoutingResponseError: ParseResponseError<RoutingAPIResponseError> = (apiError, serviceName) => {\n const { data, message, status } = apiError;\n const errorMessage = data?.error?.description ?? data?.detailedError?.message ?? message;\n return new SDKServiceError(errorMessage, serviceName, status);\n};\n","import { isNil } from 'lodash-es';\nimport type {\n CombustionEngineModel,\n ConsumptionModelEfficiency,\n ElectricConsumptionModel,\n ElectricEngineModel,\n SpeedToConsumptionRate,\n} from '../types/vehicleEngineParams';\nimport type { VehicleParameters } from '../types/vehicleParams';\nimport type { ChargingPreferences, ChargingPreferencesKWH, ChargingPreferencesPCT } from '../types/vehiclePreferences';\nimport type { CombustionVehicleState, ElectricVehicleStateKWH, ElectricVehicleStatePCT } from '../types/vehicleState';\n\n/**\n * Converts a percentage of a battery's capacity to the kWh figure every charge parameter takes.\n * @ignore\n */\nexport const kWhFromPercentage = (maxChargeKWH: number, percentage: number): number =>\n (maxChargeKWH * percentage) / 100;\n\n/**\n * The consumption, charge and charging-preference query parameters, keyed by the name the API takes\n * them under. Every routing endpoint spells these identically.\n *\n * @ignore\n */\nexport type EnergyQueryParams = Record<string, string>;\n\n/**\n * Whether the caller described an electric vehicle.\n *\n * @remarks\n * `VehicleParameters` is only partly discriminated — the generic member carries no `engineType` —\n * so the tag has to be tested for presence before it can be compared.\n * @ignore\n */\nexport const isElectricVehicle = (\n vehicle: VehicleParameters,\n): vehicle is Extract<VehicleParameters, { engineType: 'electric' }> =>\n 'engineType' in vehicle && vehicle.engineType === 'electric';\n\n/**\n * The engine of an electric vehicle the caller spelled out. A vehicle named by `model.variantId` has\n * none: the service holds its consumption and battery model.\n * @ignore\n */\nexport const electricEngineOf = (vehicle: VehicleParameters): ElectricEngineModel | undefined => {\n if (!isElectricVehicle(vehicle) || !vehicle.model || 'variantId' in vehicle.model) return undefined;\n\n return vehicle.model.engine;\n};\n\n/** The engine of a combustion vehicle, which is always spelled out. @ignore */\nexport const combustionEngineOf = (vehicle: VehicleParameters): CombustionEngineModel | undefined =>\n 'engineType' in vehicle && vehicle.engineType === 'combustion' ? vehicle.model?.engine : undefined;\n\nconst withParameter = (parameters: EnergyQueryParams, name: string, value: number | string | undefined): void => {\n if (!isNil(value)) parameters[name] = String(value);\n};\n\n// e.g. 50,6.3:130,11.5\nconst speedToConsumptionString = (rates: SpeedToConsumptionRate[]): string =>\n rates.map((rate) => `${rate.speedKMH},${rate.consumptionUnitsPer100KM}`).join(':');\n\n/** Efficiency parameters, which are spelled the same for either engine type. @ignore */\nexport const efficiencyParams = (efficiency?: ConsumptionModelEfficiency): EnergyQueryParams => {\n const parameters: EnergyQueryParams = {};\n if (!efficiency) return parameters;\n\n withParameter(parameters, 'accelerationEfficiency', efficiency.acceleration);\n withParameter(parameters, 'decelerationEfficiency', efficiency.deceleration);\n withParameter(parameters, 'uphillEfficiency', efficiency.uphill);\n withParameter(parameters, 'downhillEfficiency', efficiency.downhill);\n\n return parameters;\n};\n\n/** @ignore */\nexport const electricConsumptionParams = (consumption: ElectricConsumptionModel): EnergyQueryParams => {\n const parameters: EnergyQueryParams = {};\n if (consumption.speedsToConsumptionsKWH?.length)\n parameters.constantSpeedConsumptionInkWhPerHundredkm = speedToConsumptionString(\n consumption.speedsToConsumptionsKWH,\n );\n\n withParameter(parameters, 'auxiliaryPowerInkW', consumption.auxiliaryPowerInkW);\n withParameter(parameters, 'consumptionInkWhPerkmAltitudeGain', consumption.consumptionInKWHPerKMAltitudeGain);\n withParameter(parameters, 'recuperationInkWhPerkmAltitudeLoss', consumption.recuperationInKWHPerKMAltitudeLoss);\n\n return { ...parameters, ...efficiencyParams(consumption.efficiency) };\n};\n\n/** @ignore */\nexport const combustionConsumptionParams = (engine: CombustionEngineModel): EnergyQueryParams => {\n const consumption = engine.consumption;\n const parameters: EnergyQueryParams = {};\n if (consumption.speedsToConsumptionsLiters?.length)\n parameters.constantSpeedConsumptionInLitersPerHundredkm = speedToConsumptionString(\n consumption.speedsToConsumptionsLiters,\n );\n\n withParameter(parameters, 'auxiliaryPowerInLitersPerHour', consumption.auxiliaryPowerInLitersPerHour);\n withParameter(parameters, 'fuelEnergyDensityInMJoulesPerLiter', consumption.fuelEnergyDensityInMJoulesPerLiter);\n\n return { ...parameters, ...efficiencyParams(consumption.efficiency) };\n};\n\n/**\n * The battery capacity declared on an explicit electric model, which every percentage-based charge\n * and charging preference is converted against.\n *\n * @ignore\n */\nexport const maxChargeKWHOf = (vehicle: VehicleParameters): number | undefined =>\n electricEngineOf(vehicle)?.charging?.maxChargeKWH;\n\n/** @ignore */\nexport const maxChargeParams = (engine: ElectricEngineModel): EnergyQueryParams => {\n const parameters: EnergyQueryParams = {};\n if (engine.charging?.maxChargeKWH) parameters.maxChargeInkWh = String(engine.charging.maxChargeKWH);\n\n return parameters;\n};\n\n/** @ignore */\nexport const electricEngineParams = (engine: ElectricEngineModel): EnergyQueryParams => ({\n ...electricConsumptionParams(engine.consumption),\n ...maxChargeParams(engine),\n});\n\n/**\n * The charge a vehicle sets off with, always as kWh: a percentage is only meaningful once converted\n * against the battery capacity, so a percentage without one yields nothing.\n *\n * @remarks\n * A flat battery is a charge the API takes, and a different request from one that says nothing about\n * the charge — hence the nil tests rather than truthiness.\n * @ignore\n */\nexport const currentChargeKWHOf = (vehicle: VehicleParameters): number | undefined => {\n const absoluteCharge = (vehicle.state as ElectricVehicleStateKWH | undefined)?.currentChargeInkWh;\n if (!isNil(absoluteCharge)) return absoluteCharge;\n\n const percentage = (vehicle.state as ElectricVehicleStatePCT | undefined)?.currentChargePCT;\n const maxChargeKWH = maxChargeKWHOf(vehicle);\n if (!isNil(percentage) && maxChargeKWH) return kWhFromPercentage(maxChargeKWH, percentage);\n\n return undefined;\n};\n\n/** {@link currentChargeKWHOf} as the query parameter the API takes it under. @ignore */\nexport const currentChargeParams = (vehicle: VehicleParameters): EnergyQueryParams => {\n const parameters: EnergyQueryParams = {};\n withParameter(parameters, 'currentChargeInkWh', currentChargeKWHOf(vehicle));\n\n return parameters;\n};\n\n/** @ignore */\nexport const currentFuelParams = (vehicle: VehicleParameters): EnergyQueryParams => {\n const parameters: EnergyQueryParams = {};\n withParameter(\n parameters,\n 'currentFuelInLiters',\n (vehicle.state as CombustionVehicleState | undefined)?.currentFuelInLiters,\n );\n\n return parameters;\n};\n\n/**\n * The charging stops a caller asked for, which only an electric vehicle can declare.\n *\n * @remarks\n * Charging preferences are what select the charging-stops endpoint, so the request builder asks\n * this too — reaching them one way keeps the endpoint it picks and the parameters it sends from\n * disagreeing.\n * @ignore\n */\nexport const chargingPreferencesOf = (vehicle: VehicleParameters): ChargingPreferences | undefined =>\n isElectricVehicle(vehicle) ? vehicle.preferences?.chargingPreferences : undefined;\n\n/**\n * The minimum charge to keep at stops and on arrival, always as kWh. Absolute values win; percentages\n * need the battery capacity to convert against, and yield nothing without one.\n *\n * @ignore\n */\nexport const chargingPreferenceParams = (vehicle: VehicleParameters): EnergyQueryParams => {\n const preferences = chargingPreferencesOf(vehicle);\n const parameters: EnergyQueryParams = {};\n if (!preferences) return parameters;\n\n const absolutePreferences = preferences as ChargingPreferencesKWH;\n if (\n !isNil(absolutePreferences.minChargeAtDestinationInkWh) ||\n !isNil(absolutePreferences.minChargeAtChargingStopsInkWh)\n ) {\n withParameter(parameters, 'minChargeAtDestinationInkWh', absolutePreferences.minChargeAtDestinationInkWh);\n withParameter(parameters, 'minChargeAtChargingStopsInkWh', absolutePreferences.minChargeAtChargingStopsInkWh);\n\n return parameters;\n }\n\n const maxChargeKWH = maxChargeKWHOf(vehicle);\n if (!maxChargeKWH) return parameters;\n\n const percentagePreferences = preferences as ChargingPreferencesPCT;\n if (!isNil(percentagePreferences.minChargeAtDestinationPCT))\n parameters.minChargeAtDestinationInkWh = String(\n kWhFromPercentage(maxChargeKWH, percentagePreferences.minChargeAtDestinationPCT),\n );\n\n if (!isNil(percentagePreferences.minChargeAtChargingStopsPCT))\n parameters.minChargeAtChargingStopsInkWh = String(\n kWhFromPercentage(maxChargeKWH, percentagePreferences.minChargeAtChargingStopsPCT),\n );\n\n return parameters;\n};\n\n/** @ignore */\nexport const setEnergyParams = (urlParams: URLSearchParams, parameters: EnergyQueryParams): void => {\n for (const [name, value] of Object.entries(parameters)) urlParams.set(name, value);\n};\n","import { currentTypes, plugTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { tollTransponderOptions } from '../types/vehicleRestrictionParams';\n\n// Common validation schemas\nconst positiveNumber = z.number().positive();\nconst nonNegativeNumber = z.number().min(0);\nconst percentageNumber = z.number().min(0).max(100);\n\n// Common optional schemas\nconst optionalPositiveNumber = positiveNumber.optional();\nconst optionalNonNegativeNumber = nonNegativeNumber.optional();\nconst optionalNormalizedNumber = z.number().min(0).max(1).optional();\n\n// Speed to consumption rate schema\nconst speedToConsumptionRateSchema = z.object({\n speedKMH: z.number(),\n consumptionUnitsPer100KM: z.number(),\n});\n\n// Efficiency schema for consumption models\nconst efficiencySchema = z\n .object({\n acceleration: optionalNormalizedNumber,\n deceleration: optionalNormalizedNumber,\n uphill: optionalNormalizedNumber,\n downhill: optionalNormalizedNumber,\n })\n .optional();\n\n// Base consumption model schema\nconst baseConsumptionModelSchema = {\n efficiency: efficiencySchema,\n};\n\n// Consumption array validation\nconst speedConsumptionArray = z.array(speedToConsumptionRateSchema).min(1).max(25);\n\n// Combustion consumption model schema\nconst combustionConsumptionModelSchema = z.object({\n ...baseConsumptionModelSchema,\n speedsToConsumptionsLiters: speedConsumptionArray,\n auxiliaryPowerInLitersPerHour: optionalNonNegativeNumber,\n fuelEnergyDensityInMJoulesPerLiter: z.number().min(1).optional(),\n});\n\n// Electric consumption model schema\nconst electricConsumptionModelSchema = z.object({\n ...baseConsumptionModelSchema,\n speedsToConsumptionsKWH: speedConsumptionArray,\n auxiliaryPowerInkW: optionalNonNegativeNumber,\n consumptionInKWHPerKMAltitudeGain: z.number().max(500).optional(),\n recuperationInKWHPerKMAltitudeLoss: optionalNonNegativeNumber,\n});\n\n// Charging connector schema\nconst chargingConnectorSchema = z.object({\n currentType: z.enum(currentTypes),\n plugTypes: z.array(z.enum(plugTypes)).min(1),\n efficiency: optionalNormalizedNumber,\n baseLoadInkW: optionalNonNegativeNumber,\n maxPowerInkW: optionalNonNegativeNumber,\n maxVoltageInV: optionalNonNegativeNumber,\n maxCurrentInA: optionalNonNegativeNumber,\n voltageRange: z\n .object({\n minVoltageInV: optionalNonNegativeNumber,\n maxVoltageInV: z.number().optional(),\n })\n .optional(),\n});\n\n// Battery curve schema\nconst batteryCurveSchema = z.object({\n stateOfChargeInkWh: nonNegativeNumber,\n maxPowerInkW: positiveNumber,\n});\n\n// Charging model schema\nconst chargingModelSchema = z.object({\n maxChargeKWH: positiveNumber,\n batteryCurve: z.array(batteryCurveSchema).max(20).optional(),\n chargingConnectors: z.array(chargingConnectorSchema).min(1).optional(),\n chargingTimeOffsetInSec: optionalNonNegativeNumber,\n});\n\n// Engine model schemas\nconst combustionEngineModelSchema = z.object({\n consumption: combustionConsumptionModelSchema,\n});\nconst electricEngineModelSchema = z.object({\n consumption: electricConsumptionModelSchema,\n charging: chargingModelSchema.optional(),\n});\n\n// Vehicle dimensions schema\nconst vehicleDimensionsSchema = z\n .object({\n weightKG: optionalPositiveNumber,\n })\n .optional();\n\n// Vehicle model schemas\nconst predefinedVehicleModelSchema = z.object({\n variantId: z.string(),\n});\nconst explicitVehicleModelSchema = z.object({\n dimensions: vehicleDimensionsSchema,\n engine: z.union([combustionEngineModelSchema, electricEngineModelSchema]).optional(),\n});\nconst vehicleModelSchema = z.union([predefinedVehicleModelSchema, explicitVehicleModelSchema]).optional();\n\n// Vehicle state schemas\nconst genericVehicleStateSchema = z.object({\n heading: z.number().min(0).max(360).optional(),\n});\n\nconst combustionVehicleStateSchema = genericVehicleStateSchema.extend({\n currentFuelInLiters: nonNegativeNumber,\n});\n\nconst electricVehicleStateByPercentageSchema = genericVehicleStateSchema.extend({\n currentChargePCT: percentageNumber,\n});\n\nconst electricVehicleStateByKwhSchema = genericVehicleStateSchema.extend({\n currentChargeInkWh: nonNegativeNumber,\n});\n\nconst electricVehicleStateSchema = z.union([electricVehicleStateByPercentageSchema, electricVehicleStateByKwhSchema]);\n\n// Charging preferences schemas\nconst chargingPreferencesPCTSchema = z.object({\n minChargeAtDestinationPCT: percentageNumber,\n minChargeAtChargingStopsPCT: z.number().min(0).max(50),\n});\n\nconst chargingPreferencesKWHSchema = z.object({\n minChargeAtDestinationInkWh: nonNegativeNumber,\n minChargeAtChargingStopsInkWh: nonNegativeNumber,\n});\n\nconst chargingPreferencesSchema = z.union([chargingPreferencesPCTSchema, chargingPreferencesKWHSchema]);\nconst electricVehiclePreferencesSchema = z.object({\n chargingPreferences: chargingPreferencesSchema.optional(),\n});\n\n// Vehicle restrictions schema\nconst vehicleRestrictionsSchema = z\n .object({\n maxSpeedKMH: z.number().min(0).max(250).optional(),\n tollTransponder: z.enum(tollTransponderOptions).optional(),\n })\n .optional();\n\n// Base vehicle parameters schema\nconst baseVehicleParamsSchema = {\n model: vehicleModelSchema,\n restrictions: vehicleRestrictionsSchema,\n};\n\n// Vehicle parameters schemas\nconst genericVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.undefined().optional(),\n state: genericVehicleStateSchema.optional(),\n});\n\nconst combustionVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.literal('combustion'),\n state: combustionVehicleStateSchema.optional(),\n});\n\nconst electricVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.literal('electric'),\n state: electricVehicleStateSchema.optional(),\n preferences: electricVehiclePreferencesSchema.optional(),\n});\n\n/**\n * @ignore\n */\nexport const vehicleParametersSchema = z.union([\n z.discriminatedUnion('engineType', [combustionVehicleParamsSchema, electricVehicleParamsSchema]),\n genericVehicleParamsSchema,\n]);\n","import { avoidableTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { routeTypes } from '../types/commonRoutingParams';\nimport { hasBBoxSchema } from './geometriesSchema';\nimport { vehicleParametersSchema } from './vehicleParamsSchema';\n\n/**\n * @ignore\n */\nexport const costModelSchema = z.object({\n avoid: z.array(z.enum(avoidableTypes)).optional(),\n traffic: z.enum(['live', 'historical']).optional(),\n routeType: z.enum(routeTypes).optional(),\n avoidAreas: z.array(hasBBoxSchema).max(10).optional(),\n});\n\n/**\n * @ignore\n */\nexport const departArriveSchema = z.object({\n option: z.enum(['departAt', 'arriveBy']),\n date: z.date(),\n});\n\n/**\n * @ignore\n */\nexport const commonRoutingRequestSchema = z.object({\n costModel: costModelSchema.optional(),\n travelMode: z.string().optional(),\n vehicle: vehicleParametersSchema.optional(),\n when: departArriveSchema.optional(),\n});\n","import type { Avoidable, HasLngLat } from '@tomtom-org/maps-sdk/core';\nimport type {\n CombustionVehicleParams,\n CombustionVehicleState,\n CommonRoutingParams,\n CommonServiceParams,\n CostModel,\n DepartArriveParams,\n ElectricVehicleParams,\n ElectricVehicleState,\n GenericVehicleParams,\n VehicleParameters,\n VehicleRestrictions,\n} from '../../shared';\nimport type { ReachableRangeRequestAPI } from './apiRequestTypes';\nimport type { ReachableRangeResponseAPI } from './apiResponseTypes';\n\nexport const budgetTypes = [\n 'timeMinutes',\n 'remainingChargePCT',\n 'spentChargePCT',\n 'spentFuelLiters',\n 'distanceKM',\n] as const;\n\n/**\n * Type of budget constraint for reachable range calculation.\n *\n * @remarks\n * **Budget Types:**\n * - `timeMinutes`: Travel time in minutes (isochrone)\n * - `distanceKM`: Travel distance in kilometers (isodistance)\n * - `remainingChargePCT`: Remaining battery charge in percentage (EV)\n * - `spentChargePCT`: Battery charge consumed in percentage (EV)\n * - `spentFuelLiters`: Fuel consumed in liters (combustion)\n *\n * Both charge budgets are converted against the battery capacity, so they need an electric\n * `vehicle` with `model.engine.charging.maxChargeKWH`; `remainingChargePCT` also needs its current\n * charge in `vehicle.state`.\n *\n * **Common Use Cases:**\n * - Time: \"Where can I reach in 30 minutes?\"\n * - Distance: \"What's within 10 km?\"\n * - EV charge: \"How far can I go on 50% battery?\"\n * - Fuel: \"Range with 20 liters of fuel\"\n *\n * @example\n * ```typescript\n * const timeType: BudgetType = 'timeMinutes';\n * const distanceType: BudgetType = 'distanceKM';\n * const evType: BudgetType = 'remainingChargePCT';\n * ```\n *\n * @group Reachable Range\n */\nexport type BudgetType = (typeof budgetTypes)[number];\n\n/**\n * Budget constraint for reachable range calculation.\n *\n * Defines the limit (time, distance, or fuel/charge) for calculating\n * how far you can travel from a starting point.\n *\n * @remarks\n * **Use Cases:**\n * - Service area visualization (30-min delivery zone)\n * - EV range anxiety mitigation (show reachable area)\n * - Emergency response coverage (10-min response time)\n * - Delivery zone planning\n * - Store location analysis\n *\n * @example\n * ```typescript\n * // 30-minute travel time\n * const timeBudget: ReachableRangeBudget = {\n * type: 'timeMinutes',\n * value: 30\n * };\n *\n * // 50 km distance\n * const distanceBudget: ReachableRangeBudget = {\n * type: 'distanceKM',\n * value: 50\n * };\n *\n * // 50% battery remaining\n * const evBudget: ReachableRangeBudget = {\n * type: 'remainingChargePCT',\n * value: 50\n * };\n *\n * // 20 liters of fuel\n * const fuelBudget: ReachableRangeBudget = {\n * type: 'spentFuelLiters',\n * value: 20\n * };\n * ```\n *\n * @group Reachable Range\n */\nexport type ReachableRangeBudget = {\n /**\n * The type of budget, including units. See {@link BudgetType}.\n */\n type: BudgetType;\n\n /**\n * The value of the budget based on the units mentioned in the type.\n *\n * @remarks\n * **Typical Values:**\n * - Time: 5-60 minutes\n * - Distance: 5-100 km\n * - Charge: 10-100 percentage\n * - Fuel: 5-50 liters\n *\n * @example\n * ```typescript\n * value: 30 // 30 minutes/km/percent/liters depending on type\n * value: 15.5 // Decimal values supported\n * ```\n */\n value: number;\n};\n\n/**\n * Route features `calculateReachableRange` can avoid.\n *\n * @remarks\n * Every {@link Avoidable} except `alreadyUsedRoads`, which this endpoint rejects with\n * *\"Avoid alreadyUsedRoads not supported for calculateReachableRange\"*. It stays available on\n * `calculateRoute`.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeAvoidable = Exclude<Avoidable, 'alreadyUsedRoads'>;\n\n/**\n * Cost model accepted by `calculateReachableRange`.\n *\n * @remarks\n * {@link CostModel} without `avoidAreas`, which this endpoint has no parameter for, and with the\n * narrower {@link ReachableRangeAvoidable} avoid list.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeCostModel = Omit<CostModel, 'avoid' | 'avoidAreas'> & {\n /**\n * Route features to avoid when calculating the range.\n */\n avoid?: ReachableRangeAvoidable[];\n};\n\n/**\n * Vehicle parameters accepted by `calculateReachableRange`.\n *\n * @remarks\n * {@link VehicleParameters} without the two things this endpoint has no parameter for:\n *\n * - `state.heading`, which it rejects with *\"Unknown JSON field '.vehicleHeadingInDegrees'\"*. The\n * routing API takes it under that name.\n * - `preferences`, whose only member is `chargingPreferences`. Those belong to the charging-stops\n * endpoint, and a range has no stops to plan. A predefined `model.variantId` therefore needs\n * nothing beside it here, unlike on `calculateRoute`.\n *\n * Both stay available on `calculateRoute`. Everything else carries over, including the\n * engine-specific `state` variants — an electric vehicle picks between the percentage and kWh\n * forms.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeVehicleParameters = (\n | Omit<GenericVehicleParams, 'state' | 'preferences'>\n | (Omit<CombustionVehicleParams, 'state' | 'preferences'> & { state?: CombustionVehicleState })\n | (Omit<ElectricVehicleParams, 'state' | 'preferences'> & { state?: ElectricVehicleState })\n) &\n VehicleRestrictions;\n\n/**\n * Parameters specific to reachable range calculation.\n *\n * @remarks\n * These parameters are combined with common routing parameters to calculate\n * the reachable area from a starting point.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeOwnParams = {\n /**\n * How the routes underlying the range are optimized, and what they avoid.\n */\n costModel?: ReachableRangeCostModel;\n\n /**\n * Parameters for the vehicle whose reachable range is calculated.\n */\n vehicle?: ReachableRangeVehicleParameters;\n\n /**\n * Location from which the range calculation should start.\n *\n * @remarks\n * The center point from which reachability is calculated. Can be:\n * - An object with `lon` and `lat` properties\n * - An array `[longitude, latitude]`\n *\n * @example\n * ```typescript\n * // Object format\n * origin: { lon: 4.9, lat: 52.3 }\n *\n * // Array format\n * origin: [4.9, 52.3]\n * ```\n */\n origin: HasLngLat;\n\n /**\n * The budget for the reachable range calculation.\n *\n * @remarks\n * Consists of a type indicating whether it's about time, distance, or\n * fuel/charge and the units, plus its value.\n *\n * Determines the extent of the reachable area polygon.\n */\n budget: ReachableRangeBudget;\n\n /**\n * Maximum ferry length in meters to consider.\n *\n * @remarks\n * Ferries longer than this value will be avoided in the calculation.\n * Useful for excluding long ferry routes that might extend the range\n * unrealistically.\n *\n * @example\n * ```typescript\n * maxFerryLengthMeters: 5000 // Avoid ferries longer than 5 km\n * ```\n */\n maxFerryLengthMeters?: number;\n\n /**\n * Applies post-processing to smooth the polygon boundary for better visual appearance.\n *\n * @remarks\n * Stronger smoothing generally results in more points.\n *\n * @default `'strong'`\n *\n * @example\n * ```typescript\n * smoothing: 'strong'\n * ```\n */\n smoothing?: 'none' | 'weak' | 'strong';\n\n /**\n * Specifies when to depart.\n *\n * @remarks\n * If past dates are supplied or dates that are impossible to achieve,\n * it will default to departing now.\n *\n * **Traffic Impact:**\n * - Future departure times use predictive traffic\n * - Current time uses live traffic\n * - Historic times use historical patterns\n *\n * @default Depart now\n *\n * @example\n * ```typescript\n * when: { option: 'departAt', date: new Date('2025-10-20T08:00:00Z') }\n * ```\n */\n when?: DepartArriveParams<'departAt'>;\n};\n\n/**\n * Complete parameters for calculating a reachable range.\n *\n * Combines common service parameters, routing parameters, and reachable range\n * specific options to compute an isochrone or isodistance polygon.\n *\n * @remarks\n * **What it Returns:**\n * A polygon representing the area reachable from the origin within the\n * specified budget (time, distance, or fuel/charge).\n *\n * **Use Cases:**\n * - Delivery zone visualization\n * - Service area mapping\n * - Emergency response coverage\n * - EV range display\n * - Store catchment areas\n * - Real estate search (30-min commute)\n *\n * **Traffic Consideration:**\n * Results vary based on departure time and traffic conditions. Use\n * appropriate `when` values for accurate predictions.\n *\n * @example\n * ```typescript\n * // 30-minute driving range in live traffic\n * const params: ReachableRangeParams = {\n * origin: [4.9, 52.3],\n * budget: { type: 'timeMinutes', value: 30 },\n * costModel: { routeType: 'fast', traffic: 'live' }\n * };\n *\n * // EV range down to 50% remaining battery\n * const evParams: ReachableRangeParams = {\n * origin: [4.9, 52.3],\n * budget: { type: 'remainingChargePCT', value: 50 },\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * engine: {\n * consumption: {\n * speedsToConsumptionsKWH: [{ speedKMH: 80, consumptionUnitsPer100KM: 12 }]\n * },\n * charging: { maxChargeKWH: 100 }\n * }\n * },\n * state: { currentChargePCT: 80 }\n * }\n * };\n *\n * // Avoid toll roads\n * const noTollParams: ReachableRangeParams = {\n * origin: [4.9, 52.3],\n * budget: { type: 'timeMinutes', value: 45 },\n * costModel: { avoid: ['tollRoads'] }\n * };\n * ```\n *\n * @group Reachable Range\n */\nexport type ReachableRangeParams = CommonServiceParams<ReachableRangeRequestAPI, ReachableRangeResponseAPI> &\n Omit<CommonRoutingParams, 'costModel' | 'vehicle'> &\n ReachableRangeOwnParams;\n\n/**\n * The `properties` of a reachable range polygon: the parameters it was calculated with.\n *\n * @remarks\n * {@link ReachableRangeParams} without any {@link CommonServiceParams} — the API key, base URLs,\n * callbacks and abort signal never travel with the result.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeProperties = Omit<ReachableRangeParams, keyof CommonServiceParams>;\n","import { avoidableTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { currentChargeKWHOf, maxChargeKWHOf } from '../shared/request/vehicleEnergyParams';\nimport {\n commonRoutingRequestSchema,\n costModelSchema,\n departArriveSchema,\n} from '../shared/schema/commonRoutingRequestSchema';\nimport { hasLngLatSchema } from '../shared/schema/geometriesSchema';\nimport { vehicleParametersSchema } from '../shared/schema/vehicleParamsSchema';\nimport type { SchemaRefinement } from '../shared/types/validation';\nimport { type BudgetType, budgetTypes, type ReachableRangeParams } from './types/reachableRangeParams';\n\nconst reachableRangeRequestSchemaMandatory = z.object({\n origin: hasLngLatSchema,\n budget: z.object({\n type: z.enum(budgetTypes),\n value: z.number().min(0),\n }),\n});\n\n// Each narrowing below mirrors one the types already make, for callers who are not type-checked:\n// a parameter the endpoint answers `400` for fails at the call instead of being stripped in\n// silence. Each message names what to do instead.\n\nconst reachableRangeAvoidableSchema = z.enum(avoidableTypes).exclude(['alreadyUsedRoads']);\n\n// Matches `ReachableRangeVehicleParameters`, probed 2026-09-01. Charging preferences encode to\n// `minChargeAt*InkWh`, which only the charging-stops endpoint takes — this one plans no stops.\nconst reachableRangeVehicleSchema = vehicleParametersSchema.check(\n z.refine(\n (vehicle) => !(vehicle.state && 'heading' in vehicle.state),\n 'vehicle.state.heading: calculateReachableRange has no vehicle heading parameter. Remove it, or use calculateRoute, which accepts it.',\n ),\n z.refine(\n (vehicle) =>\n !(vehicle.engineType === 'electric' && vehicle.preferences && 'chargingPreferences' in vehicle.preferences),\n 'vehicle.preferences.chargingPreferences: calculateReachableRange plans no charging stops. Remove them, or use calculateRoute, which plans stops for them.',\n ),\n);\n\n// Matches `ReachableRangeParams.when`, which is departure-only.\nconst reachableRangeWhenSchema = departArriveSchema.check(\n z.refine(\n (when) => when.option !== 'arriveBy',\n 'when.arriveBy: calculateReachableRange has no arrival-time parameter. Use when.departAt instead, or omit when entirely.',\n ),\n);\n\n// Matches `ReachableRangeCostModel`. `avoidAreas` accepts anything so that the refinement, not a\n// shape mismatch, is what reports it.\nconst reachableRangeCostModelSchema = costModelSchema\n .extend({\n avoid: z.array(reachableRangeAvoidableSchema).optional(),\n avoidAreas: z.unknown().optional(),\n })\n .check(\n z.refine(\n (costModel) => costModel.avoidAreas === undefined,\n 'costModel.avoidAreas: calculateReachableRange has no avoid-areas parameter. Remove it — the other cost-model options are accepted.',\n ),\n );\n\nconst reachableRangeRequestSchemaOptional = z.object({\n when: reachableRangeWhenSchema.optional(),\n vehicle: reachableRangeVehicleSchema.optional(),\n costModel: reachableRangeCostModelSchema.optional(),\n maxFerryLengthMeters: z.number().min(0).optional(),\n smoothing: z.enum(['none', 'weak', 'strong']).optional(),\n});\n\nconst reachableRangeRequestSchema = commonRoutingRequestSchema.extend(\n reachableRangeRequestSchemaMandatory.extend(reachableRangeRequestSchemaOptional.shape).shape,\n);\n\n// Both charge budgets are a percentage of the battery and reach the wire as `energyBudgetInkWh`, so\n// without a capacity to convert against the request goes out with no budget at all. `spentFuelLiters`\n// needs no conversion and carries no such requirement.\n//\n// Each asks the request builder's own readers rather than re-deciding what is convertible, so what\n// validation accepts and what the builder can encode cannot drift apart.\nconst chargeBudgetTypes = new Set<BudgetType>(['spentChargePCT', 'remainingChargePCT']);\n\nconst batteryCapacityRefinement: SchemaRefinement<ReachableRangeParams> = {\n check: (data: ReachableRangeParams): boolean =>\n !chargeBudgetTypes.has(data.budget.type) || (!!data.vehicle && maxChargeKWHOf(data.vehicle) !== undefined),\n message:\n \"budget: a charge budget is a percentage of the battery, so it needs a capacity to convert against. Set vehicle.engineType to 'electric' and vehicle.model.engine.charging.maxChargeKWH, or budget by time or distance instead.\",\n};\n\nconst currentChargeRefinement: SchemaRefinement<ReachableRangeParams> = {\n check: (data: ReachableRangeParams): boolean =>\n data.budget.type !== 'remainingChargePCT' || (!!data.vehicle && currentChargeKWHOf(data.vehicle) !== undefined),\n message:\n 'budget.remainingChargePCT: a range down to a remaining charge is measured from the charge the vehicle sets off with. Set vehicle.state.currentChargeInkWh or vehicle.state.currentChargePCT, or use spentChargePCT to budget a share of the battery instead.',\n};\n\n/**\n * @ignore\n */\nexport const reachableRangeRequestValidationConfig = {\n schema: reachableRangeRequestSchema,\n refinements: [batteryCapacityRefinement, currentChargeRefinement],\n};\n","import type { CommonRoutingBodyAPI, CommonVehicleBodyAPI } from '../types/apiRoutingRequestTypes';\nimport type { CommonRoutingParams, CostModel } from '../types/commonRoutingParams';\nimport type { VehicleParameters } from '../types/vehicleParams';\nimport {\n combustionConsumptionParams,\n combustionEngineOf,\n currentChargeParams,\n currentFuelParams,\n electricEngineOf,\n electricEngineParams,\n isElectricVehicle,\n setEnergyParams,\n} from './vehicleEnergyParams';\n\n// Typed on what this builder reads rather than on one endpoint's params type, so the\n// reachable-range params — whose `when` is departure-only and whose cost model has no\n// `avoidAreas` — satisfy it without a cast.\ntype CommonRoutingBodyInput = Pick<CommonRoutingParams, 'travelMode' | 'when'> & {\n costModel?: Omit<CostModel, 'avoidAreas'>;\n};\n\n/**\n * Builds the body fields shared by every routing endpoint.\n * @ignore\n */\nexport const buildCommonRoutingBody = (params: CommonRoutingBodyInput): CommonRoutingBodyAPI => ({\n ...(params.costModel?.routeType && { routeType: params.costModel.routeType }),\n ...(params.costModel?.traffic && { traffic: params.costModel.traffic }),\n ...(params.costModel?.avoid?.length && { avoids: params.costModel.avoid }),\n ...(params.travelMode && { travelMode: params.travelMode }),\n ...(params.when?.option === 'departAt' &&\n params.when.date && { departureDateTime: params.when.date.toISOString() }),\n});\n\n/**\n * The `variantId` of a vehicle named from the Vehicle Database rather than spelled out.\n * @ignore\n */\nexport const predefinedModelIdOf = (vehicle: VehicleParameters): string | undefined =>\n vehicle.model && 'variantId' in vehicle.model ? vehicle.model.variantId : undefined;\n\n/**\n * Builds the vehicle body fields shared by every routing endpoint.\n * @ignore\n */\nexport const buildCommonVehicleBody = (vehicle: VehicleParameters): CommonVehicleBodyAPI => {\n const body: CommonVehicleBodyAPI = {};\n if (isElectricVehicle(vehicle)) body.vehicleEngineType = 'electric';\n\n if (vehicle.model && !('variantId' in vehicle.model) && vehicle.model.dimensions?.weightKG)\n body.vehicleWeightInKilograms = vehicle.model.dimensions.weightKG;\n\n if (vehicle.restrictions?.maxSpeedKMH) body.vehicleMaxSpeedInKilometersPerHour = vehicle.restrictions.maxSpeedKMH;\n\n if (vehicle.restrictions?.tollTransponder)\n body.vehicleHasElectronicTollCollectionTransponder = vehicle.restrictions.tollTransponder;\n\n return body;\n};\n\n/**\n * Appends the consumption model and the energy the vehicle sets off with, as query parameters.\n *\n * @remarks\n * Every routing endpoint takes both in the query rather than the body, and under the same names, so\n * all of them append them this way. The two are independent: a vehicle named by `model.variantId`\n * carries no consumption model of its own — the service holds it — and still reports its charge or\n * fuel level.\n * @ignore\n */\nexport const appendVehicleEnergyParams = (url: URL, vehicle: VehicleParameters): void => {\n const electricEngine = electricEngineOf(vehicle);\n const combustionEngine = combustionEngineOf(vehicle);\n setEnergyParams(\n url.searchParams,\n isElectricVehicle(vehicle)\n ? { ...(electricEngine && electricEngineParams(electricEngine)), ...currentChargeParams(vehicle) }\n : { ...(combustionEngine && combustionConsumptionParams(combustionEngine)), ...currentFuelParams(vehicle) },\n );\n};\n\n/**\n * {@link appendVehicleEnergyParams}, plus the `vehicleModelId` of a predefined model. Only\n * `/calculateLongDistanceEVRoute` and `/calculateReachableRange` take the latter; `/routes/calculate`\n * answers `400` for it.\n * @ignore\n */\nexport const appendVehicleQueryParams = (url: URL, vehicle: VehicleParameters): void => {\n const variantId = predefinedModelIdOf(vehicle);\n if (variantId) url.searchParams.set('vehicleModelId', variantId);\n\n appendVehicleEnergyParams(url, vehicle);\n};\n","import { getPositionStrict, type TomTomAPIHeaders } from '@tomtom-org/maps-sdk/core';\nimport { appendOptionalParam, buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport {\n appendVehicleQueryParams,\n buildCommonRoutingBody,\n buildCommonVehicleBody,\n} from '../shared/request/routingBodyBuilder';\nimport { currentChargeKWHOf, kWhFromPercentage, maxChargeKWHOf } from '../shared/request/vehicleEnergyParams';\nimport type { ReachableRangePOSTDataAPI, ReachableRangeRequestAPI } from './types/apiRequestTypes';\nimport type { ReachableRangeParams } from './types/reachableRangeParams';\n\n// The one response field the parser reads. Naming it rather than asking for the whole\n// `reachableRange` object keeps a field added to the endpoint later off the wire.\nconst ATTRIBUTES = 'reachableRange(boundary)';\n\nconst buildUrl = (params: ReachableRangeParams): URL =>\n new URL(params.customServiceBaseURL ?? `${params.commonBaseURL}/maps/orbis/routing/calculateReachableRange`);\n\n// The endpoint requires exactly one budget, as a query parameter on the POST.\nconst appendBudget = (urlParams: URLSearchParams, params: ReachableRangeParams): void => {\n const budget = params.budget;\n const maxChargeKWH = params.vehicle && maxChargeKWHOf(params.vehicle);\n switch (budget.type) {\n case 'timeMinutes':\n urlParams.append('timeBudgetInSec', (budget.value * 60).toString());\n break;\n case 'distanceKM':\n urlParams.append('distanceBudgetInMeters', (budget.value * 1000).toString());\n break;\n case 'spentFuelLiters':\n urlParams.append('fuelBudgetInLiters', budget.value.toString());\n break;\n case 'spentChargePCT':\n if (maxChargeKWH)\n urlParams.append('energyBudgetInkWh', kWhFromPercentage(maxChargeKWH, budget.value).toString());\n\n break;\n case 'remainingChargePCT': {\n const currentChargeKWH = params.vehicle && currentChargeKWHOf(params.vehicle);\n if (maxChargeKWH && currentChargeKWH !== undefined) {\n const remainingKWH = kWhFromPercentage(maxChargeKWH, budget.value);\n urlParams.append('energyBudgetInkWh', Math.max(0, currentChargeKWH - remainingKWH).toString());\n }\n break;\n }\n }\n};\n\nconst buildPostData = (params: ReachableRangeParams): ReachableRangePOSTDataAPI => ({\n origin: { type: 'Point', coordinates: getPositionStrict(params.origin) },\n ...buildCommonRoutingBody(params),\n ...(params.vehicle && buildCommonVehicleBody(params.vehicle)),\n});\n\nconst buildRequestHeaders = (params: ReachableRangeParams): TomTomAPIHeaders => ({\n ...buildCommonServiceRequestHeaders(params),\n Attributes: ATTRIBUTES,\n});\n\n/**\n * Default function for building a calculate reachable range request from\n * {@link ReachableRangeParams}, targeting the Orbis routing API.\n * @param params The reachable range parameters, with global configuration already merged into them.\n */\nexport const buildReachableRangeRequest = (params: ReachableRangeParams): ReachableRangeRequestAPI => {\n const url = buildUrl(params);\n appendBudget(url.searchParams, params);\n appendOptionalParam(url.searchParams, 'maxFerryLengthInMeters', params.maxFerryLengthMeters);\n appendOptionalParam(url.searchParams, 'smoothing', params.smoothing ?? 'strong');\n if (params.vehicle) appendVehicleQueryParams(url, params.vehicle);\n\n return { method: 'POST', url, data: buildPostData(params), headers: buildRequestHeaders(params) };\n};\n","import type { BBox, PolygonFeature } from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport type { Polygon } from 'geojson';\nimport { pick } from 'lodash-es';\nimport type { ReachableRangeResponseAPI } from './types/apiResponseTypes';\nimport type { ReachableRangeParams, ReachableRangeProperties } from './types/reachableRangeParams';\n\n// An allow-list, so credentials and anything else merged in from the global config never reach the result.\n// Written as a record, so a request parameter added to the type without an entry here fails to compile.\nconst propertyKeys = Object.keys({\n origin: true,\n budget: true,\n costModel: true,\n vehicle: true,\n travelMode: true,\n when: true,\n maxFerryLengthMeters: true,\n smoothing: true,\n} satisfies Record<keyof ReachableRangeProperties, true>) as (keyof ReachableRangeProperties)[];\n\n/**\n * @param params The request parameters, with global configuration already merged into them.\n */\nexport const parseReachableRangeResponse = (\n apiResponse: ReachableRangeResponseAPI,\n params: ReachableRangeParams,\n): PolygonFeature<ReachableRangeProperties> => {\n const geometry: Polygon = {\n type: 'Polygon',\n coordinates: [apiResponse.reachableRange.boundary.map((point) => [point.longitude, point.latitude])],\n };\n const bbox = bboxFromGeoJSON(geometry) as BBox; // the geometry should always have a bbox\n return { type: 'Feature', geometry, bbox, properties: pick(params, propertyKeys) };\n};\n","import type { PolygonFeature } from '@tomtom-org/maps-sdk/core';\nimport { parseRoutingResponseError } from '../routing/routingResponseErrorParser';\nimport type { ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { reachableRangeRequestValidationConfig } from './reachableRangeRequestSchema';\nimport { buildReachableRangeRequest } from './requestBuilder';\nimport { parseReachableRangeResponse } from './responseParser';\nimport type { ReachableRangeRequestAPI } from './types/apiRequestTypes';\nimport type { ReachableRangeResponseAPI } from './types/apiResponseTypes';\nimport type { ReachableRangeParams, ReachableRangeProperties } from './types/reachableRangeParams';\n\n/**\n * Reachable range service template type.\n * @ignore\n */\nexport type ReachableRangeTemplate = ServiceTemplate<\n ReachableRangeParams,\n ReachableRangeRequestAPI,\n ReachableRangeResponseAPI,\n PolygonFeature<ReachableRangeProperties>\n>;\n\n/**\n * Reachable range service template main implementation.\n * @ignore\n */\nexport const reachableRangeTemplate: ReachableRangeTemplate = {\n requestValidation: reachableRangeRequestValidationConfig,\n buildRequest: buildReachableRangeRequest,\n sendRequest: fetchWith,\n parseResponse: parseReachableRangeResponse,\n parseResponseError: parseRoutingResponseError,\n getAPIVersion: () => 3,\n};\n","import { bboxFromGeoJSON, PolygonFeature, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport { SDKServiceError } from '../shared/errors';\nimport { callService } from '../shared/serviceTemplate';\nimport { reachableRangeTemplate } from './reachableRangeTemplate';\nimport type { ReachableRangeParams, ReachableRangeProperties } from './types/reachableRangeParams';\n\n/**\n * Calculate the area reachable from a starting point within given constraints.\n *\n * The Reachable Range service (also known as isochrone) computes a polygon representing\n * all locations that can be reached from an origin within specified limits of time, distance,\n * or energy consumption. This is essential for range analysis, service area visualization,\n * and logistics planning.\n *\n * @remarks\n * **Private Preview**: needs an API key with Calculate Reachable Range access, unavailable on a Freemium or\n * Pay As You Grow (PAYG) basis: [request it from TomTom Sales](https://www.tomtom.com/contact-sales/?source_app=docs-portal&source_product=calculate-reachable-range).\n *\n * Key features:\n * - **Time-based ranges**: Areas reachable within a time budget (e.g., 30 minutes)\n * - **Distance-based ranges**: Areas within a distance limit (e.g., 50km)\n * - **Energy-based ranges**: EV range considering battery consumption\n * - **Traffic awareness**: Accounts for current and historic traffic patterns\n * - **Vehicle-specific**: Considers vehicle characteristics and constraints\n *\n * Common use cases:\n * - **Service area mapping**: \"Show areas we can deliver to within 1 hour\"\n * - **EV range visualization**: Display drivable range on current battery\n * - **Real estate**: \"Find homes within 45 minutes of workplace\"\n * - **Emergency services**: Calculate ambulance/fire truck response areas\n * - **Market analysis**: Identify customer catchment areas\n * - **Location planning**: Optimize facility placement based on coverage\n *\n * @param params Reachable range parameters including origin and budget constraints\n *\n * @returns Promise resolving to a polygon representing the reachable area\n *\n * @example\n * ```typescript\n * // Calculate 30-minute drive time range\n * const range30min = await calculateReachableRange({\n * origin: [4.9041, 52.3676], // Amsterdam\n * budget: { type: 'timeMinutes', value: 30 }\n * });\n *\n * // EV range down to 20% remaining battery\n * const evRange = await calculateReachableRange({\n * origin: [4.9041, 52.3676],\n * budget: { type: 'remainingChargePCT', value: 20 },\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * engine: {\n * consumption: {\n * speedsToConsumptionsKWH: [\n * { speedKMH: 50, consumptionUnitsPer100KM: 8 },\n * { speedKMH: 80, consumptionUnitsPer100KM: 12 }\n * ]\n * },\n * charging: { maxChargeKWH: 60 }\n * }\n * },\n * state: { currentChargePCT: 80 }\n * }\n * });\n *\n * // Traffic-aware range at departure time\n * const morningRange = await calculateReachableRange({\n * origin: [4.9041, 52.3676],\n * budget: { type: 'timeMinutes', value: 45 },\n * when: { option: 'departAt', date: new Date('2025-10-20T08:00:00Z') } // Rush hour\n * });\n * ```\n *\n * @see [Reachable Range API Documentation](https://developer.tomtom.com/routing-api/documentation/tomtom-orbis-maps/v3/calculate-reachable-range) (requires an enterprise login)\n * @see [Reachable Ranges Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/routing/reachable-ranges)\n *\n * @group Routing\n */\nexport const calculateReachableRange = async (\n params: ReachableRangeParams,\n): Promise<PolygonFeature<ReachableRangeProperties>> => callService(params, reachableRangeTemplate, 'Reachable Range');\n\n// Either signal cancels the call. A lone signal passes through unwrapped, so a caller still\n// sees the identical signal it handed in.\nconst combineSignals = (...signals: (AbortSignal | undefined)[]): AbortSignal | undefined => {\n const live = signals.filter((signal): signal is AbortSignal => !!signal);\n return live.length < 2 ? live[0] : AbortSignal.any(live);\n};\n\n/**\n * Calculate multiple reachable range areas from different origins or with different constraints.\n *\n * Computes several isochrone polygons in sequence, useful for comparing ranges from multiple\n * locations, visualizing multiple time/distance rings from the same origin, or analyzing\n * different vehicle scenarios.\n *\n * @remarks\n * **Private Preview**: needs the same API key access as {@link calculateReachableRange}.\n *\n * Use cases:\n * - **Multi-location comparison**: Compare service areas of different store locations\n * - **Concentric ranges**: Create 15/30/45 minute drive time rings from one origin\n * - **Scenario analysis**: Compare ranges for different vehicle types or times of day\n * - **Coverage optimization**: Find optimal facility locations with minimal overlap\n *\n * Note: Ranges are calculated sequentially to avoid rate limiting. For large batches,\n * consider implementing your own parallel processing with appropriate throttling.\n *\n * @param paramsArray Array of reachable range parameters, one for each area to calculate\n * @param options.signal An `AbortSignal` to cancel the in-flight request and skip the remaining ones.\n * Composed with any per-entry `signal`, so either one cancels.\n *\n * @returns Promise resolving to a FeatureCollection of reachable area polygons\n *\n * @example\n * ```typescript\n * // Calculate concentric drive time rings (15, 30, 45 minutes)\n * const timeRings = await calculateReachableRanges([\n * { origin: [4.9, 52.3], budget: { type: 'timeMinutes', value: 15 } },\n * { origin: [4.9, 52.3], budget: { type: 'timeMinutes', value: 30 } },\n * { origin: [4.9, 52.3], budget: { type: 'timeMinutes', value: 45 } }\n * ]);\n *\n * // Compare service areas of multiple stores\n * const storeRanges = await calculateReachableRanges([\n * { origin: [4.9, 52.3], budget: { type: 'timeMinutes', value: 30 } },\n * { origin: [4.5, 51.9], budget: { type: 'timeMinutes', value: 30 } },\n * { origin: [5.1, 52.1], budget: { type: 'timeMinutes', value: 30 } }\n * ]);\n *\n * // Compare the range spent on 10% versus 20% of the battery\n * const electricVehicle = {\n * engineType: 'electric' as const,\n * model: {\n * engine: {\n * consumption: { speedsToConsumptionsKWH: [{ speedKMH: 80, consumptionUnitsPer100KM: 12 }] },\n * charging: { maxChargeKWH: 60 }\n * }\n * }\n * };\n * const batteryRanges = await calculateReachableRanges([\n * { origin: [4.9, 52.3], budget: { type: 'spentChargePCT', value: 10 }, vehicle: electricVehicle },\n * { origin: [4.9, 52.3], budget: { type: 'spentChargePCT', value: 20 }, vehicle: electricVehicle }\n * ]);\n * ```\n *\n * @see [Reachable Range API Documentation](https://developer.tomtom.com/routing-api/documentation/tomtom-orbis-maps/v3/calculate-reachable-range) (requires an enterprise login)\n * @see [Reachable Ranges Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/routing/reachable-ranges)\n *\n * @group Routing\n */\nexport const calculateReachableRanges = async (\n paramsArray: ReachableRangeParams[],\n options?: { signal?: AbortSignal },\n): Promise<PolygonFeatures<ReachableRangeProperties>> => {\n const features: PolygonFeature<ReachableRangeProperties>[] = [];\n for (const params of paramsArray) {\n const signal = combineSignals(params.signal, options?.signal);\n try {\n // we sequentially fetch reachable ranges (less speed but better to prevent QPS limit breaches):\n features.push(await calculateReachableRange({ ...params, ...(signal && { signal }) }));\n } catch (error) {\n // Re-throw non-API errors (e.g. validation/programming errors) and critical HTTP errors\n // (403 Forbidden, 429 Too Many Requests) — only silently skip API errors that indicate\n // no reachable range could be computed at this location (e.g. no road network).\n if (!(error instanceof SDKServiceError) || error.status === 403 || error.status === 429) {\n throw error;\n }\n }\n }\n const bbox = bboxFromGeoJSON(features);\n return { type: 'FeatureCollection', ...(bbox && { bbox }), features };\n};\n","import type { GeographyType } from '@tomtom-org/maps-sdk/core';\nimport { getPositionStrict } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport type { GetObject } from '../shared';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { ReverseGeocodingAreaTypeAPI } from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n// Maps the SDK's public GeographyType values to the API's `areaTypes` values.\nexport const AREA_TYPE_BY_GEOGRAPHY_TYPE: Record<GeographyType, ReverseGeocodingAreaTypeAPI> = {\n Country: 'country',\n CountrySubdivision: 'countrySubdivision',\n CountrySecondarySubdivision: 'countrySecondarySubdivision',\n CountryTertiarySubdivision: 'countryTertiarySubdivision',\n Municipality: 'municipality',\n MunicipalitySubdivision: 'municipalitySubdivision',\n MunicipalitySecondarySubdivision: 'municipalitySecondarySubdivision',\n Neighbourhood: 'neighborhood',\n PostalCodeArea: 'postalCode',\n};\n\n// Requests every result field the SDK's response parser understands, so callers get a\n// complete place without having to specify a projection themselves.\nconst DEFAULT_ATTRIBUTES = 'results(*)';\n\nconst buildUrlBasePath = (params: ReverseGeocodingParams): string => resolvePlacesEndpointUrl(params, 'reverseGeocode');\n\n/**\n * Default function for building a reverse geocoding request from {@link ReverseGeocodingParams}\n * @param params The reverse geocoding parameters, with global configuration already merged into them.\n */\nexport const buildRevGeoRequest = (params: ReverseGeocodingParams): GetObject => {\n const [lng, lat] = getPositionStrict(params.position);\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n urlParams.append('position', `${lng},${lat}`);\n\n !isNil(params.radiusMeters) && urlParams.append('radiusInMeters', String(params.radiusMeters));\n params.geographyTypes &&\n urlParams.append(\n 'areaTypes',\n params.geographyTypes.map((geographyType) => AREA_TYPE_BY_GEOGRAPHY_TYPE[geographyType]).join(','),\n );\n !isNil(params.heading) && urlParams.append('vehicleHeadingInDegrees', String(params.heading));\n params.view && urlParams.append('geopoliticalView', params.view);\n\n return {\n url,\n headers: {\n ...buildCommonServiceRequestHeaders(params),\n Attributes: DEFAULT_ATTRIBUTES,\n },\n };\n};\n","const ISO2_TO_ISO3: Record<string, string> = {\n AF: 'AFG',\n AX: 'ALA',\n AL: 'ALB',\n DZ: 'DZA',\n AS: 'ASM',\n AD: 'AND',\n AO: 'AGO',\n AI: 'AIA',\n AQ: 'ATA',\n AG: 'ATG',\n AR: 'ARG',\n AM: 'ARM',\n AW: 'ABW',\n AU: 'AUS',\n AT: 'AUT',\n AZ: 'AZE',\n BS: 'BHS',\n BH: 'BHR',\n BD: 'BGD',\n BB: 'BRB',\n BY: 'BLR',\n BE: 'BEL',\n BZ: 'BLZ',\n BJ: 'BEN',\n BM: 'BMU',\n BT: 'BTN',\n BO: 'BOL',\n BQ: 'BES',\n BA: 'BIH',\n BW: 'BWA',\n BV: 'BVT',\n BR: 'BRA',\n IO: 'IOT',\n BN: 'BRN',\n BG: 'BGR',\n BF: 'BFA',\n BI: 'BDI',\n CV: 'CPV',\n KH: 'KHM',\n CM: 'CMR',\n CA: 'CAN',\n KY: 'CYM',\n CF: 'CAF',\n TD: 'TCD',\n CL: 'CHL',\n CN: 'CHN',\n CX: 'CXR',\n CC: 'CCK',\n CO: 'COL',\n KM: 'COM',\n CG: 'COG',\n CD: 'COD',\n CK: 'COK',\n CR: 'CRI',\n CI: 'CIV',\n HR: 'HRV',\n CU: 'CUB',\n CW: 'CUW',\n CY: 'CYP',\n CZ: 'CZE',\n DK: 'DNK',\n DJ: 'DJI',\n DM: 'DMA',\n DO: 'DOM',\n EC: 'ECU',\n EG: 'EGY',\n SV: 'SLV',\n GQ: 'GNQ',\n ER: 'ERI',\n EE: 'EST',\n SZ: 'SWZ',\n ET: 'ETH',\n FK: 'FLK',\n FO: 'FRO',\n FJ: 'FJI',\n FI: 'FIN',\n FR: 'FRA',\n GF: 'GUF',\n PF: 'PYF',\n TF: 'ATF',\n GA: 'GAB',\n GM: 'GMB',\n GE: 'GEO',\n DE: 'DEU',\n GH: 'GHA',\n GI: 'GIB',\n GR: 'GRC',\n GL: 'GRL',\n GD: 'GRD',\n GP: 'GLP',\n GU: 'GUM',\n GT: 'GTM',\n GG: 'GGY',\n GN: 'GIN',\n GW: 'GNB',\n GY: 'GUY',\n HT: 'HTI',\n HM: 'HMD',\n VA: 'VAT',\n HN: 'HND',\n HK: 'HKG',\n HU: 'HUN',\n IS: 'ISL',\n IN: 'IND',\n ID: 'IDN',\n IR: 'IRN',\n IQ: 'IRQ',\n IE: 'IRL',\n IM: 'IMN',\n IL: 'ISR',\n IT: 'ITA',\n JM: 'JAM',\n JP: 'JPN',\n JE: 'JEY',\n JO: 'JOR',\n KZ: 'KAZ',\n KE: 'KEN',\n KI: 'KIR',\n KP: 'PRK',\n KR: 'KOR',\n KW: 'KWT',\n KG: 'KGZ',\n LA: 'LAO',\n LV: 'LVA',\n LB: 'LBN',\n LS: 'LSO',\n LR: 'LBR',\n LY: 'LBY',\n LI: 'LIE',\n LT: 'LTU',\n LU: 'LUX',\n MO: 'MAC',\n MG: 'MDG',\n MW: 'MWI',\n MY: 'MYS',\n MV: 'MDV',\n ML: 'MLI',\n MT: 'MLT',\n MH: 'MHL',\n MQ: 'MTQ',\n MR: 'MRT',\n MU: 'MUS',\n YT: 'MYT',\n MX: 'MEX',\n FM: 'FSM',\n MD: 'MDA',\n MC: 'MCO',\n MN: 'MNG',\n ME: 'MNE',\n MS: 'MSR',\n MA: 'MAR',\n MZ: 'MOZ',\n MM: 'MMR',\n NA: 'NAM',\n NR: 'NRU',\n NP: 'NPL',\n NL: 'NLD',\n NC: 'NCL',\n NZ: 'NZL',\n NI: 'NIC',\n NE: 'NER',\n NG: 'NGA',\n NU: 'NIU',\n NF: 'NFK',\n MK: 'MKD',\n MP: 'MNP',\n NO: 'NOR',\n OM: 'OMN',\n PK: 'PAK',\n PW: 'PLW',\n PS: 'PSE',\n PA: 'PAN',\n PG: 'PNG',\n PY: 'PRY',\n PE: 'PER',\n PH: 'PHL',\n PN: 'PCN',\n PL: 'POL',\n PT: 'PRT',\n PR: 'PRI',\n QA: 'QAT',\n RE: 'REU',\n RO: 'ROU',\n RU: 'RUS',\n RW: 'RWA',\n BL: 'BLM',\n SH: 'SHN',\n KN: 'KNA',\n LC: 'LCA',\n MF: 'MAF',\n PM: 'SPM',\n VC: 'VCT',\n WS: 'WSM',\n SM: 'SMR',\n ST: 'STP',\n SA: 'SAU',\n SN: 'SEN',\n RS: 'SRB',\n SC: 'SYC',\n SL: 'SLE',\n SG: 'SGP',\n SX: 'SXM',\n SK: 'SVK',\n SI: 'SVN',\n SB: 'SLB',\n SO: 'SOM',\n ZA: 'ZAF',\n GS: 'SGS',\n SS: 'SSD',\n ES: 'ESP',\n LK: 'LKA',\n SD: 'SDN',\n SR: 'SUR',\n SJ: 'SJM',\n SE: 'SWE',\n CH: 'CHE',\n SY: 'SYR',\n TW: 'TWN',\n TJ: 'TJK',\n TZ: 'TZA',\n TH: 'THA',\n TL: 'TLS',\n TG: 'TGO',\n TK: 'TKL',\n TO: 'TON',\n TT: 'TTO',\n TN: 'TUN',\n TR: 'TUR',\n TM: 'TKM',\n TC: 'TCA',\n TV: 'TUV',\n UG: 'UGA',\n UA: 'UKR',\n AE: 'ARE',\n GB: 'GBR',\n US: 'USA',\n UM: 'UMI',\n UY: 'URY',\n UZ: 'UZB',\n VU: 'VUT',\n VE: 'VEN',\n VN: 'VNM',\n VG: 'VGB',\n VI: 'VIR',\n WF: 'WLF',\n EH: 'ESH',\n YE: 'YEM',\n ZM: 'ZMB',\n ZW: 'ZWE',\n};\n\nexport const toIso3 = (iso2: string): string => ISO2_TO_ISO3[iso2] ?? iso2;\n","import type { GeographyType } from '@tomtom-org/maps-sdk/core';\nimport { generateId, getPositionStrict, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { toIso3 } from '../shared/iso2ToIso3';\nimport { AREA_TYPE_BY_GEOGRAPHY_TYPE } from './requestBuilder';\nimport type { ReverseGeocodingResponse } from './reverseGeocoding';\nimport type {\n ReverseGeocodingAddressAPI,\n ReverseGeocodingAreaTypeAPI,\n ReverseGeocodingResponseAPI,\n ReverseGeocodingResultTypeAPI,\n} from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\nconst PLACE_TYPE_BY_RESULT_TYPE: Record<ReverseGeocodingResultTypeAPI, 'Street' | 'Geography' | 'Point Address'> = {\n street: 'Street',\n area: 'Geography',\n address: 'Point Address',\n};\n\nconst GEOGRAPHY_TYPE_BY_AREA_TYPE = Object.fromEntries(\n Object.entries(AREA_TYPE_BY_GEOGRAPHY_TYPE).map(([geographyType, areaType]) => [areaType, geographyType]),\n) as Record<ReverseGeocodingAreaTypeAPI, GeographyType>;\n\n// Renames the handful of v2 address fields whose SDK-facing name differs from the API's, and\n// derives `countryCodeISO3`, which v2 does not return but the v1 search services do. Same\n// derivation the routing response parser applies to its own `countryCodeIso2` fields.\nconst mapAddress = (apiAddress: ReverseGeocodingAddressAPI) => {\n const { street, houseNumber, countryCodeIso2, ...rest } = apiAddress;\n return {\n ...rest,\n ...(street && { streetName: street }),\n ...(houseNumber && { streetNumber: houseNumber }),\n ...(countryCodeIso2 && { countryCode: countryCodeIso2, countryCodeISO3: toIso3(countryCodeIso2) }),\n };\n};\n\n/**\n * Default method for parsing reverse geocoding request from {@link ReverseGeocodingResponse}\n * @param params\n * @param apiResponse\n */\nexport const parseRevGeoResponse = (\n apiResponse: ReverseGeocodingResponseAPI,\n params: ReverseGeocodingParams,\n): ReverseGeocodingResponse => {\n const firstResult = apiResponse.results[0];\n return {\n type: 'Feature',\n // The requested coordinates are the primary ones, and set as the GeoJSON Feature geometry:\n geometry: toPointGeometry(getPositionStrict(params.position)),\n id: firstResult?.id ?? generateId(),\n ...(firstResult && {\n properties: {\n type: PLACE_TYPE_BY_RESULT_TYPE[firstResult.type],\n address: { freeformAddress: firstResult.title, ...mapAddress(firstResult.address) },\n ...(firstResult.areaType && {\n geographyType: [GEOGRAPHY_TYPE_BY_AREA_TYPE[firstResult.areaType]],\n // v2 has no `dataSources`, but for an area result its `id` is the geometry ID.\n // Surfacing it here keeps `getGeometryData` / `GeometriesModule` reachable from a\n // reverse geocoded geography, as it is from a search result.\n dataSources: { geometry: { id: firstResult.id } },\n }),\n // v2 calls them access points and places them on the road network; they are the\n // same entrances the search services return, minus the main/minor classification.\n ...(firstResult.accessPoints?.length && {\n entryPoints: firstResult.accessPoints.map((accessPoint) => ({\n position: accessPoint.position.coordinates,\n })),\n }),\n // The reverse geocoded coordinates are secondary and set in the GeoJSON properties:\n originalPosition: firstResult.position.coordinates,\n },\n }),\n };\n};\n","import { geographyTypes, views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { hasLngLatSchema } from '../shared/schema/geometriesSchema';\n\nconst revGeocodeRequestMandatory = z.object({\n position: hasLngLatSchema,\n});\n\nconst revGeocodeRequestOptional = z.object({\n geographyTypes: z.array(z.enum(geographyTypes)).optional(),\n heading: z.number().min(-360).max(360).optional(),\n radiusMeters: z.number().positive().max(5_000_000).optional(),\n view: z.enum(views).optional(),\n});\n\n/**\n * @ignore\n */\nexport const revGeocodeRequestSchema = revGeocodeRequestMandatory.extend(revGeocodeRequestOptional.shape);\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { buildRevGeoRequest } from './requestBuilder';\nimport { parseRevGeoResponse } from './responseParser';\nimport type { ReverseGeocodingResponse } from './reverseGeocoding';\nimport { revGeocodeRequestSchema } from './revGeocodeRequestSchema';\nimport type { ReverseGeocodingResponseAPI } from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n/**\n * Reverse Geocoding service template type.\n * @ignore\n */\nexport type ReverseGeocodingTemplate = ServiceTemplate<\n ReverseGeocodingParams,\n GetObject,\n ReverseGeocodingResponseAPI,\n ReverseGeocodingResponse\n>;\n\n/**\n * Reverse Geocoding service template main implementation.\n * @ignore\n */\nexport const reverseGeocodingTemplate: ReverseGeocodingTemplate = {\n requestValidation: { schema: revGeocodeRequestSchema },\n buildRequest: buildRevGeoRequest,\n getAPIVersion: () => 2,\n sendRequest: get,\n parseResponse: parseRevGeoResponse,\n};\n","import type { Place, RevGeoAddressProps } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { reverseGeocodingTemplate } from './reverseGeocodingTemplate';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n/**\n * Response from the reverse geocoding service.\n *\n * Contains a place with address information for the given coordinates.\n *\n * @group Reverse Geocoding\n */\nexport type ReverseGeocodingResponse = Place<RevGeoAddressProps>;\n\n/**\n * Convert geographic coordinates into human-readable addresses (reverse geocoding).\n *\n * Reverse geocoding translates latitude/longitude coordinates into street addresses,\n * which is essential for location-based applications that need to display addresses\n * from GPS coordinates or map clicks.\n *\n * @remarks\n * Common use cases:\n * - **Tracking applications**: Convert GPS coordinates from devices into addresses\n * - **Map interactions**: Display address when user clicks on map\n * - **Location sharing**: Show readable location instead of coordinates\n * - **Delivery apps**: Confirm pickup/dropoff addresses from driver location\n * - **Asset tracking**: Display current location of vehicles or equipment\n *\n * Features:\n * - Returns complete address hierarchy (street, city, state, country)\n * - Returns the result type (address, street, or geography)\n * - Narrows results to administrative areas with `geographyTypes`\n * - Direction-aware street matching with `heading`\n *\n * @param params - Reverse geocoding parameters including coordinates\n *\n * @returns Promise resolving to the address for the given coordinates\n *\n * @example\n * ```typescript\n * // Get address for coordinates\n * const address = await reverseGeocode({\n * key: 'your-api-key',\n * position: [4.9041, 52.3676] // Amsterdam coordinates\n * });\n * // Returns: Dam, 1012 Amsterdam, Netherlands\n *\n * // Get the address matched along a given travel direction\n * const specificAddress = await reverseGeocode({\n * key: 'your-api-key',\n * position: [-77.0369, 38.8977], // Washington DC\n * heading: 90 // Traveling east\n * });\n *\n * // Get the containing municipality instead of the street\n * const municipality = await reverseGeocode({\n * key: 'your-api-key',\n * position: [-74.0060, 40.7128], // New York\n * geographyTypes: ['Municipality']\n * });\n * ```\n *\n * @see [Reverse Geocode API Documentation](https://docs.tomtom.com/search-api/documentation/reverse-geocoding-service/reverse-geocode)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Reverse Geocoding Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/reverse-geocoding)\n *\n * @group Reverse Geocoding\n */\nexport const reverseGeocode = async (params: ReverseGeocodingParams): Promise<ReverseGeocodingResponse> =>\n callService(params, reverseGeocodingTemplate, 'ReverseGeocode');\n","import {\n bboxFromGeoJSON,\n getPositionStrict,\n getRoutePlanningLocationType,\n type HasBBox,\n type HasLngLat,\n type LegSectionProps,\n type PathLike,\n type RoutePlanningLocation,\n type TomTomAPIHeaders,\n type WaypointLike,\n type WaypointProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { Feature, LineString, MultiPoint, Point } from 'geojson';\nimport { isNil } from 'lodash-es';\nimport type { FetchInput, VehicleParameters } from '../shared';\nimport { buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport {\n appendVehicleEnergyParams,\n appendVehicleQueryParams,\n buildCommonRoutingBody,\n buildCommonVehicleBody,\n predefinedModelIdOf,\n} from '../shared/request/routingBodyBuilder';\nimport {\n chargingPreferenceParams,\n chargingPreferencesOf,\n electricEngineOf,\n setEnergyParams,\n} from '../shared/request/vehicleEnergyParams';\nimport type {\n AvoidAreasAPI,\n CalculateRoutePOSTDataAPI,\n LegRequestAPI,\n RouteStopRequestAPI,\n} from './types/apiRequestTypes';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\nimport type { RouteStopOptions } from './types/routeStopOptions';\n\nconst buildAvoidAreas = (avoidAreas: HasBBox[]): AvoidAreasAPI => ({\n rectangles: avoidAreas.map((rectangle) => {\n const bbox = bboxFromGeoJSON(rectangle);\n if (!bbox) throw new Error('Could not derive a bounding box from an avoidAreas rectangle');\n\n const [minLon, minLat, maxLon, maxLat] = bbox;\n return { type: 'Feature' as const, bbox: [minLon, minLat, maxLon, maxLat], geometry: null };\n }),\n});\n\n// Section types that are EXPLICIT — they must be individually listed in the Attributes header\n// to be included in the response, even when the parent `sections` is already requested.\n// `lanes` is also EXPLICIT but is guidance-only and handled separately below.\nconst EXPLICIT_SECTION_TYPES = ['tollVignette', 'tollRoad', 'roadShields', 'importantRoadStretch'] as const;\n\nconst buildAttributesHeader = (params: CalculateRouteParams): string => {\n const parts = ['summary', 'legs(summary,path)'];\n const sectionTypes = params.sectionTypes;\n // All non-EXPLICIT section types come back when sections is requested.\n // Omit sections only when the caller explicitly passes an empty sectionTypes array.\n const requestSections = !sectionTypes || sectionTypes.length > 0;\n if (requestSections) {\n parts.push('sections');\n // EXPLICIT section sub-attributes must be listed individually in the Attributes header.\n // When sectionTypes is undefined the customer wants everything, so request all EXPLICIT types\n // (preserving V2 behavior). When sectionTypes is a specific list, only add the requested ones.\n const wantAll = !sectionTypes;\n for (const type of EXPLICIT_SECTION_TYPES) {\n if (wantAll || sectionTypes.includes(type)) {\n parts.push(`sections.${type}`);\n }\n }\n // sections.lanes is EXPLICIT and only available with guidance.\n if (params.guidance && (wantAll || sectionTypes.includes('lanes'))) {\n parts.push('sections.lanes');\n }\n }\n // There is no per-representation `extendedRouteRepresentation` parameter: `progressPoints`\n // is all-or-nothing, so a non-empty `extendedRouteRepresentations` array coarsens to \"give me\n // progress points\". An explicitly empty array is the caller opting out, and saves the payload.\n if (params.extendedRouteRepresentations?.length !== 0) parts.push('progressPoints');\n // When guidance is requested, include the EXPLICIT instruction sub-attributes that back\n // fields the V2 SDK exposed: road-shield icon references, signpost exit icon\n // (→ signpostRoadShieldReferences), and the traffic-light offset.\n if (params.guidance) {\n parts.push(\n 'instructions(*,nextRoadInformation.roadShields.iconReference,previousRoadInformation.roadShields.iconReference,signpost.exitIconReference,distanceToPreviousTrafficLightInMeters)',\n );\n }\n // `roadShieldAtlasReference` is an EXPLICIT top-level attribute (sibling of `routes`), used to\n // build road-shield images; request it whenever guidance instructions are requested.\n const routesAttr = `routes(${parts.join(',')})`;\n return params.guidance ? `roadShieldAtlasReference,${routesAttr}` : routesAttr;\n};\n\n// ─── Regular route (/routes/calculate) vehicle body helper ───\n\n// Every field but the heading is shared with the other two endpoints. `/calculateReachableRange`\n// answers `400 Unknown JSON field` for `vehicleHeadingInDegrees`, so it is added only here.\nconst buildVehicleBody = (vehicle: VehicleParameters): Partial<CalculateRoutePOSTDataAPI> => {\n // 0 is due north, so the heading is tested for presence rather than truthiness.\n const heading = vehicle.state?.heading;\n\n return {\n ...buildCommonVehicleBody(vehicle),\n ...(!isNil(heading) && { vehicleHeadingInDegrees: heading }),\n };\n};\n\n// ─── LDEVR (/calculateLongDistanceEVRoute) URL param helpers ─────────────────\n\ntype LDEVRParams = CalculateRouteParams & { vehicle: VehicleParameters };\n\nconst appendLDEVRVehicleParams = (url: URL, vehicle: VehicleParameters): void => {\n appendVehicleQueryParams(url, vehicle);\n setEnergyParams(url.searchParams, chargingPreferenceParams(vehicle));\n};\n\n// Charging preferences alone select this endpoint. `chargingStopsStrategy` deliberately does NOT:\n// LDEVR also requires `minChargeAtDestinationInkWh`, which only the preferences supply, so\n// triggering the endpoint on the strategy alone builds a request the API is guaranteed to reject.\n// The pairing is enforced in the request schema instead, which calls this same predicate so that\n// the endpoint the builder picks and the request the schema accepts can never disagree.\nexport const isLDEVRRequest = (params: CalculateRouteParams): params is LDEVRParams =>\n !!params.vehicle && !!chargingPreferencesOf(params.vehicle);\n\nexport const PREDEFINED_MODEL_WITHOUT_LDEVR_MESSAGE =\n 'vehicle.model.variantId is only supported for EV routes with charging stops: set vehicle.preferences.chargingPreferences, or describe the vehicle with vehicle.model.dimensions and vehicle.model.engine instead.';\n\n// ─── Route reconstruction helpers ────────────────────────────────────────────\n\n// The wait lives on core's `WaypointProps`, since the map reads it too; the rest is routing-only.\ntype StopOptions = WaypointProps & RouteStopOptions;\n\n// The builder's own intermediate, not a wire shape — hence no `API` suffix and no place in\n// apiRequestTypes.ts: the coordinate becomes `routePlanningLocations`, the options become part of\n// `legs[]`, and only `legToNext` is already wire-shaped.\ntype Stop = { coordinate: [number, number]; legToNext?: LegRequestAPI; options?: StopOptions };\n\nconst getPathCoordinates = (pathLike: PathLike): [number, number][] => {\n if (Array.isArray(pathLike)) return pathLike as [number, number][];\n\n return pathLike.geometry.coordinates as [number, number][];\n};\n\nconst getPathLegSections = (pathLike: PathLike): LegSectionProps[] | undefined => {\n if (Array.isArray(pathLike)) return undefined;\n\n return pathLike.properties?.sections?.leg;\n};\n\nconst expandPathToStops = (pathLike: PathLike): Stop[] => {\n const coordinates = getPathCoordinates(pathLike);\n const legSections = getPathLegSections(pathLike);\n if (\n legSections &&\n legSections.length > 1 &&\n legSections.every((section) => section.startPointIndex != null && section.endPointIndex != null)\n ) {\n const stops: Stop[] = legSections.map((section) => {\n const start = section.startPointIndex ?? 0;\n const end = section.endPointIndex ?? 0;\n return {\n coordinate: coordinates[start],\n legToNext: {\n path: {\n type: 'LineString',\n coordinates: coordinates.slice(start, end + 1),\n },\n },\n };\n });\n stops.push({\n coordinate: coordinates[legSections.at(-1)?.endPointIndex ?? coordinates.length - 1],\n });\n return stops;\n }\n return [\n {\n coordinate: coordinates[0],\n legToNext: { path: { type: 'LineString', coordinates } as LineString },\n },\n { coordinate: coordinates.at(-1) ?? coordinates[0] },\n ];\n};\n\n// Per-stop options ride on the `properties` of a waypoint Feature — see RouteStopOptions.\nconst getRouteStopOptions = (location: RoutePlanningLocation): StopOptions | undefined => {\n if (Array.isArray(location) || !('properties' in location)) return undefined;\n\n return (location as Feature<Point, StopOptions>).properties ?? undefined;\n};\n\nconst flattenLocations = (\n locations: RoutePlanningLocation[],\n useEntryPoint?: CalculateRouteParams['useEntryPoints'],\n): Stop[] => {\n const result: Stop[] = [];\n for (const location of locations) {\n if (getRoutePlanningLocationType(location) === 'waypoint') {\n const options = getRouteStopOptions(location);\n // Explicit candidate entry points are chosen between by the routing engine, so the\n // stop itself must stay the place center rather than a client-side entry point.\n const stopEntryPoint = options?.candidateEntryPoints?.length ? 'ignore' : useEntryPoint;\n const [longitude, latitude] = getPositionStrict(location as WaypointLike, {\n useEntryPoint: stopEntryPoint,\n });\n result.push({ coordinate: [longitude, latitude], ...(options && { options }) });\n } else {\n result.push(...expandPathToStops(location as PathLike));\n }\n }\n return result;\n};\n\nconst toEntryPointsMultiPoint = (entryPoints: HasLngLat[]): MultiPoint => ({\n type: 'MultiPoint',\n coordinates: entryPoints.map((entryPoint) => getPositionStrict(entryPoint)),\n});\n\n// Builds the `legs[]` entry for the leg *arriving at* `arrivingStop`, from that stop's options.\nconst toLegRequest = (arrivingStop: Stop): LegRequestAPI => {\n const options = arrivingStop.options;\n if (!options) return {};\n\n const routeStop: RouteStopRequestAPI = {\n ...(options.pauseDurationSeconds && { pauseDurationInSeconds: options.pauseDurationSeconds }),\n ...(options.candidateEntryPoints?.length && {\n entryPoints: toEntryPointsMultiPoint(options.candidateEntryPoints),\n ...(!isNil(options.preferredEntryPointIndex) && {\n preferredEntryPointIndex: options.preferredEntryPointIndex,\n }),\n }),\n };\n\n return {\n ...(options.legCostModel?.routeType && { routeType: options.legCostModel.routeType }),\n // The route-level `avoids` is a string array; per-leg it takes objects.\n ...(options.legCostModel?.avoid?.length && {\n avoids: options.legCostModel.avoid.map((name) => ({ name })),\n }),\n ...(Object.keys(routeStop).length > 0 && { routeStop }),\n };\n};\n\n// `legs[]` carries route-reconstruction paths and per-stop options; both triggers share the one\n// array, so it is emitted when either applies. Options belong to the stop the leg arrives at,\n// which is what keeps them on the right stop when another stop is inserted earlier in the list.\nconst buildLegs = (allStops: Stop[], hasPathLocations: boolean): LegRequestAPI[] | undefined => {\n // One leg per stop that has something before it, so the index is the departing stop's.\n const arrivingStops = allStops.slice(1);\n const legs = arrivingStops.map((arrivingStop, index) => ({\n ...allStops[index].legToNext,\n ...toLegRequest(arrivingStop),\n }));\n\n if (!hasPathLocations && legs.every((leg) => Object.keys(leg).length === 0)) return undefined;\n\n return legs;\n};\n\nconst buildRoutePlanningLocations = (allStops: Stop[]) => {\n const origin: Point = { type: 'Point', coordinates: allStops[0].coordinate };\n const destination: Point = {\n type: 'Point',\n coordinates: (allStops.at(-1) ?? allStops[0]).coordinate,\n };\n const middleStops = allStops.slice(1, -1);\n return {\n origin,\n destination,\n ...(middleStops.length && {\n waypoints: {\n type: 'MultiPoint' as const,\n coordinates: middleStops.map((stop) => stop.coordinate),\n },\n }),\n };\n};\n\n// ─── Main exported function helpers ──────────────────────────────────────────\n\n// The first spread is what all three routing endpoints share; everything after it is a field\n// `/calculateReachableRange` rejects, which is why it stays here rather than moving to the shared\n// builder.\nconst buildCommonBodyFields = (params: CalculateRouteParams) => ({\n ...buildCommonRoutingBody(params),\n ...(params.maxAlternatives && { maxPathAlternativeRoutes: params.maxAlternatives }),\n ...(params.when?.option === 'arriveBy' && params.when.date && { arrivalDateTime: params.when.date.toISOString() }),\n // The API rejects guidance without a phonetic alphabet; the parser drops the transcriptions.\n ...(params.guidance && { guidance: 'instructions' as const, instructionPhonetics: 'ipa' as const }),\n ...(params.costModel?.avoidAreas?.length && { avoidAreas: buildAvoidAreas(params.costModel.avoidAreas) }),\n ...(params.arrivalSide && {\n arrivalSidePreference: params.arrivalSide === 'curb' ? ('curbSide' as const) : ('anySide' as const),\n }),\n});\n\nconst appendTravelTimeParam = (url: URL, params: CalculateRouteParams): void => {\n if (params.computeTravelTimeFor) url.searchParams.set('computeTravelTimeFor', params.computeTravelTimeFor);\n};\n\nconst buildRequestHeaders = (params: CalculateRouteParams): TomTomAPIHeaders => ({\n ...buildCommonServiceRequestHeaders(params),\n Attributes: buildAttributesHeader(params),\n});\n\nconst buildLDEVRRequest = (\n params: LDEVRParams,\n sharedBody: CalculateRoutePOSTDataAPI,\n headers: TomTomAPIHeaders,\n): FetchInput<CalculateRoutePOSTDataAPI> => {\n const baseURL =\n params.customServiceBaseURL ?? `${params.commonBaseURL}/maps/orbis/routing/calculateLongDistanceEVRoute`;\n const url = new URL(baseURL);\n appendLDEVRVehicleParams(url, params.vehicle);\n // A query parameter, not a body field — the body rejects it as an unknown JSON field.\n if (params.chargingStopsStrategy) url.searchParams.set('chargingStopsStrategy', params.chargingStopsStrategy);\n\n const chargingModel = electricEngineOf(params.vehicle)?.charging;\n const hasPredefinedModel = !!predefinedModelIdOf(params.vehicle);\n // `chargingParameters` is mandatory on this endpoint, but the endpoint itself is selected by\n // `vehicle.preferences.chargingPreferences` — a different part of the params. Without either a\n // predefined model (the API supplies the charging model for it) or explicit connectors, the\n // request is guaranteed to fail, so fail here with a message that names the actual cause.\n if (!hasPredefinedModel && !chargingModel?.chargingConnectors?.length)\n throw new Error(\n 'Charging preferences require a charging model: set vehicle.model.engine.charging.chargingConnectors (or a predefined vehicle.model.variantId) to plan charging stops.',\n );\n\n // Only ever *adds* to the shared body: every field both endpoints take is built once, before\n // the branch, so a field added there cannot reach one endpoint and silently miss the other.\n const body: CalculateRoutePOSTDataAPI = {\n ...sharedBody,\n ...(chargingModel && {\n chargingParameters: {\n ...(chargingModel.batteryCurve?.length && { batteryCurve: chargingModel.batteryCurve }),\n ...(chargingModel.chargingConnectors?.length && {\n chargingConnectors: [...chargingModel.chargingConnectors],\n }),\n ...(!isNil(chargingModel.chargingTimeOffsetInSec) && {\n chargingTimeOffsetInSec: chargingModel.chargingTimeOffsetInSec,\n }),\n },\n }),\n // This endpoint is electric by definition, whatever the vehicle body derived.\n vehicleEngineType: 'electric',\n };\n\n return { method: 'POST', url, data: body, headers };\n};\n\n// ─── Main exported function ───────────────────────────────────────────────────\n\n/**\n * Default function for building calculate route request from {@link CalculateRouteParams}\n * targeting the Orbis routing API.\n * @param params The calculate route parameters, with global configuration already merged into them.\n */\nexport const buildCalculateRouteRequest = (params: CalculateRouteParams): FetchInput<CalculateRoutePOSTDataAPI> => {\n const hasPathLocations = params.locations.map(getRoutePlanningLocationType).includes('path');\n const allStops = flattenLocations(params.locations, params.useEntryPoints);\n const routePlanningLocations = buildRoutePlanningLocations(allStops);\n const commonBodyFields = buildCommonBodyFields(params);\n const headers = buildRequestHeaders(params);\n // Everything above, plus `legs` and the travel-time param below, is shared by both endpoints:\n // the same CalculateRouteParams must mean the same thing whether or not charging preferences\n // routed it to LDEVR.\n const legs = buildLegs(allStops, hasPathLocations);\n const sharedBody: CalculateRoutePOSTDataAPI = {\n routePlanningLocations,\n ...commonBodyFields,\n ...(legs && { legs }),\n ...(params.vehicle && buildVehicleBody(params.vehicle)),\n };\n if (isLDEVRRequest(params)) {\n const ldevrRequest = buildLDEVRRequest(params, sharedBody, headers);\n appendTravelTimeParam(ldevrRequest.url, params);\n return ldevrRequest;\n }\n\n // `vehicleModelId` is only supported by the LDEVR endpoint, which is reached by setting\n // `vehicle.preferences.chargingPreferences`. Sending it here returns\n // `400 parameter [vehicleModelId] not supported`, and dropping it silently would plan the\n // route for a default vehicle instead of the requested one.\n if (params.vehicle && predefinedModelIdOf(params.vehicle)) throw new Error(PREDEFINED_MODEL_WITHOUT_LDEVR_MESSAGE);\n\n const baseURL = params.customServiceBaseURL ?? `${params.commonBaseURL}/maps/orbis/routing/routes/calculate`;\n const url = new URL(baseURL);\n if (params.vehicle) appendVehicleEnergyParams(url, params.vehicle);\n\n appendTravelTimeParam(url, params);\n\n return { method: 'POST', url, data: sharedBody, headers };\n};\n","import type { GetPositionEntryPointOption, inputSectionTypes, RoutePlanningLocation } from '@tomtom-org/maps-sdk/core';\nimport type { CommonRoutingParams, CommonServiceParams, ElectricVehicleParamsWithChargingStops } from '../../shared';\nimport type { CalculateRouteRequestAPI } from './apiRequestTypes';\nimport type { CalculateRouteResponseAPI } from './apiResponseTypes';\n\n/**\n * Route section type that can be requested in routing parameters.\n *\n * @remarks\n * Note: Some section types (like \"leg\") are automatically included regardless of this parameter.\n *\n * @group Routing\n */\nexport type InputSectionType = (typeof inputSectionTypes)[number];\n\n/**\n * Array of section types to include in the route response.\n *\n * Sections divide the route into portions with specific characteristics, helping you\n * display additional context like toll roads, ferry crossings, or traffic incidents.\n *\n * @remarks\n * Available section types:\n * - `carTrain`: Sections requiring car train transport\n * - `ferry`: Water crossings requiring ferry\n * - `tunnel`: Underground tunnel sections\n * - `motorway`: Highway/freeway sections\n * - `pedestrian`: Pedestrian-only sections\n * - `toll`: Sections requiring toll payment\n * - `tollVignette`: Sections requiring toll vignette\n * - `country`: Different countries traversed\n * - `traffic`: Sections with traffic incidents\n * - `vehicleRestricted`: Sections with vehicle restrictions\n * - `carpool`: HOV/carpool lane sections\n * - `urban`: Urban area sections\n * - `unpaved`: Unpaved road sections\n * - `lowEmissionZone`: Low emission zones\n * - `speedLimit`: Speed limit changes\n * - `roadShields`: Road shield information\n * - `importantRoadStretch`: Named important road stretches along the route\n *\n * @example\n * ```typescript\n * // Request toll, ferry, and traffic sections\n * const sectionTypes: InputSectionTypes = ['toll', 'ferry', 'traffic'];\n *\n * // Request all available sections\n * const sectionTypes: InputSectionTypes = inputSectionTypes;\n *\n * // Request no optional sections (leg sections still included)\n * const sectionTypes: InputSectionTypes = [];\n * ```\n *\n * @remarks\n * A requested section type is only present in the response if the route actually traverses it\n * (e.g. `ferry` is omitted on a route with no water crossing). The `lanes` section is guidance-only:\n * it is returned solely when {@link GuidanceParams | `guidance`} is also requested, and is therefore\n * not part of this input list.\n *\n * @default When omitted, all section types are requested (equivalent to `inputSectionTypes`).\n *\n * @group Routing\n */\nexport type InputSectionTypes = InputSectionType[];\n\n/**\n * The accepted values of {@link ChargingStopsStrategy}.\n * @group Routing\n */\nexport const chargingStopsStrategies = [\n 'automaticFastest',\n 'manualFastest',\n 'automaticFastestWithFallbackToManual',\n] as const;\n\n/**\n * How the routing service should plan charging stops on an electric route.\n *\n * @remarks\n * - `automaticFastest`: the service picks charging stops for the fastest journey. The default.\n * - `manualFastest`: the service plans no charging stops of its own; only stops you pass as\n * waypoints are charged at.\n * - `automaticFastestWithFallbackToManual`: automatic, falling back to manual when no chargeable\n * route exists.\n *\n * Requires `vehicle.preferences.chargingPreferences` to be set as well: the endpoint also needs a\n * minimum charge at the destination, which only the preferences supply.\n * @group Routing\n */\nexport type ChargingStopsStrategy = (typeof chargingStopsStrategies)[number];\n\n/**\n * Configuration for turn-by-turn guidance instructions.\n *\n * @group Routing\n */\nexport type GuidanceParams = {\n /**\n * Guidance instruction format type.\n *\n * Currently only 'coded' format is supported.\n */\n type: 'coded';\n};\n\n/**\n * Extended route representation options.\n *\n * Controls which additional progress information is included at route polyline points.\n *\n * @remarks\n * - `distance`: Cumulative distance from start to each point\n * - `travelTime`: Cumulative travel time from start to each point\n *\n * @example\n * ```typescript\n * // Include both distance and time progress\n * const extended: ExtendedRouteRepresentation[] = ['distance', 'travelTime'];\n * ```\n *\n * @default ['distance', 'travelTime']\n *\n * @group Routing\n */\nexport type ExtendedRouteRepresentation = 'distance' | 'travelTime';\n\n/**\n * Traffic computation mode for alternative travel time estimates.\n *\n * Controls whether to calculate additional travel times using different traffic scenarios.\n *\n * @remarks\n * - `none`: Only return default best-estimate travel time\n * - `all`: Return travel times for multiple traffic scenarios:\n * - No traffic (free flow)\n * - Historic traffic patterns\n * - Live traffic incidents\n *\n * @example\n * ```typescript\n * // Get all traffic-based time estimates\n * const computeTravelTimeFor: ComputeTravelTimeFor = 'all';\n * ```\n *\n * @default 'none'\n *\n * @group Routing\n */\nexport type ComputeTravelTimeFor = 'none' | 'all';\n\n/**\n * Side of the road to arrive on.\n *\n * @group Routing\n */\nexport const arrivalSides = ['any', 'curb'] as const;\n\n/**\n * Which side of the road the route should arrive on at each stop.\n *\n * @remarks\n * - `any`: arrive from either side, whichever is faster\n * - `curb`: arrive on the curb side for the country's driving direction\n *\n * @example\n * ```typescript\n * const arrivalSide: ArrivalSide = 'curb';\n * ```\n *\n * @default 'any'\n *\n * @group Routing\n */\nexport type ArrivalSide = (typeof arrivalSides)[number];\n\n/**\n * Maximum number of alternative routes to calculate.\n *\n * Alternative routes provide different options for traveling between the same origin and destination.\n *\n * @remarks\n * - `0`: Only calculate the best route (default)\n * - `1-5`: Calculate best route plus up to N alternatives\n *\n * More alternatives increase computation time but provide more route options.\n *\n * @example\n * ```typescript\n * // Request up to 2 alternative routes\n * const maxAlternatives: MaxNumberOfAlternatives = 2;\n * ```\n *\n * @default 0\n *\n * @group Routing\n */\nexport type MaxNumberOfAlternatives = 0 | 1 | 2 | 3 | 4 | 5;\n\n/**\n * Parameters for calculating a route between waypoints.\n *\n * Includes origin, destination, optional intermediate waypoints, and various routing options\n * to customize the calculated route based on vehicle type, traffic, and preferences.\n *\n * @example\n * ```typescript\n * // Basic route from A to B\n * const params: CalculateRouteParams = {\n * apiKey: 'your-api-key',\n * locations: [\n * [4.9041, 52.3676], // Amsterdam\n * [4.4777, 51.9244] // Rotterdam\n * ]\n * };\n *\n * // Route with guidance and alternatives\n * const advancedParams: CalculateRouteParams = {\n * apiKey: 'your-api-key',\n * locations: [[4.9041, 52.3676], [4.4777, 51.9244]],\n * guidance: { type: 'coded' },\n * maxAlternatives: 2,\n * costModel: {\n * routeType: 'fast',\n * avoid: ['tollRoads', 'motorways']\n * },\n * when: {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * }\n * };\n *\n * // Electric vehicle route with charging\n * const evParams: CalculateRouteParams = {\n * apiKey: 'your-api-key',\n * locations: [[4.9, 52.3], [8.5, 50.1]],\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * engine: {\n * charging: { maxChargeKWH: 85 },\n * consumption: {\n * speedsToConsumptionsKWH: [\n * { speedKMH: 50, consumptionUnitsPer100KM: 8 },\n * { speedKMH: 80, consumptionUnitsPer100KM: 12 },\n * { speedKMH: 120, consumptionUnitsPer100KM: 18 }\n * ]\n * }\n * }\n * },\n * state: { currentChargeInkWh: 50 }\n * }\n * };\n * ```\n *\n * @group Routing\n */\nexport type CalculateRouteParams = (\n | { chargingStopsStrategy?: never }\n | {\n /**\n * How the service should plan charging stops on an electric route.\n *\n * Only the charging-stops endpoint understands it, and\n * `vehicle.preferences.chargingPreferences` is what selects that endpoint — hence the\n * vehicle this variant requires.\n *\n * @default 'automaticFastest' (the service's own default when unset)\n *\n * @example\n * ```typescript\n * // Plan the fastest journey, choosing charging stops along the way\n * chargingStopsStrategy: 'automaticFastest'\n * ```\n */\n chargingStopsStrategy: ChargingStopsStrategy;\n\n /** An electric vehicle whose charging preferences ask the service to plan stops. */\n vehicle: ElectricVehicleParamsWithChargingStops;\n }\n) &\n CommonServiceParams<CalculateRouteRequestAPI, CalculateRouteResponseAPI> &\n CommonRoutingParams & {\n /**\n * Ordered list of locations (waypoints) and/or path points for route calculation.\n *\n * The route will pass through these locations in the specified order.\n *\n * @remarks\n * Requirements:\n * - Minimum 2 waypoints (origin and destination) OR 1 path with 2+ points\n *\n * Supported formats:\n * - Coordinate arrays: `[longitude, latitude]`\n * - Path arrays for route reconstruction\n * - Waypoint Features, whose properties carry the per-stop options\n *\n * The route ends a leg at every location in this list. There is no circle (soft) waypoint\n * that the route merely passes: the routing API takes a plain point for each one. A path\n * pins the geometry the route must follow, but its endpoints go out as waypoints as well,\n * so it shapes the route without shortening the list of legs.\n *\n * @see [POST data parameters](https://docs.tomtom.com/routing-api/documentation/tomtom-maps/calculate-route#post-data-parameters)\n * @see [Response structure](https://docs.tomtom.com/routing-api/documentation/tomtom-maps/calculate-route#structure-of-a-successful-response)\n *\n * @example\n * ```typescript\n * // Coordinate arrays\n * locations: [[4.9, 52.3], [4.5, 51.9]]\n *\n * // With intermediate stop\n * locations: [[4.9, 52.3], [4.7, 52.1], [4.5, 51.9]]\n *\n * // Path array for route reconstruction\n * locations: [\n * [4.9, 52.3], // Origin waypoint\n * [\n * [4.85, 52.25], [4.80, 52.20], [4.75, 52.15] // Path points between waypoints\n * ],\n * [4.5, 51.9] // Destination waypoint\n * ]\n * ```\n */\n locations: RoutePlanningLocation[];\n\n /**\n * Controls how entry points are used for routing.\n *\n * Entry points represent specific building entrances or facility access points.\n * Using them improves routing accuracy by directing to the correct entrance.\n *\n * @remarks\n * - `main-when-available`: Use main entry point if available, otherwise use place center (recommended for routing)\n * - `ignore`: Always use place center position\n *\n * @default 'main-when-available'\n *\n * @example\n * ```typescript\n * // Route to main entrance when available\n * useEntryPoints: 'main-when-available'\n * ```\n */\n useEntryPoints?: GetPositionEntryPointOption;\n\n /**\n * Which side of the road the route should arrive on.\n *\n * @remarks\n * - `any`: arrive from either side, whichever is faster\n * - `curb`: arrive on the curb side for the country's driving direction, so passengers or\n * deliveries can leave the vehicle without crossing the road\n *\n * Applies to the destination and to every intermediate stop.\n *\n * @default 'any'\n *\n * @example\n * ```typescript\n * // Pull up on the passenger side at each stop\n * arrivalSide: 'curb'\n * ```\n */\n arrivalSide?: ArrivalSide;\n\n /**\n * Request travel time calculations for different traffic scenarios.\n *\n * When set to 'all', the returned route summary will contain extra fields:\n * - `noTrafficTravelTimeInSeconds` – Free-flow (no traffic)\n * - `historicTrafficTravelTimeInSeconds` – Historic traffic patterns\n * - `liveTrafficIncidentsTravelTimeInSeconds` – Current live traffic\n *\n * Useful for comparing traffic impact and displaying \"X minutes saved by leaving now\".\n *\n * @default 'none'\n *\n * @example\n * ```typescript\n * computeTravelTimeFor: 'all'\n * ```\n */\n computeTravelTimeFor?: ComputeTravelTimeFor;\n\n /**\n * Request extended progress information at route polyline points.\n *\n * Includes cumulative distance and/or time from start to each coordinate.\n * When non-empty, the route features will contain additional property \"progress\" with an array of objects containing:\n * - `pointIndex` – Index of the coordinate in the route geometry\n * - `travelTimeInSeconds` - Cumulative travel time from start to this point (if \"travelTime\" is requested)\n * - `distanceInMeters` - Cumulative distance from start to this point (if \"distance\" is requested)\n *\n * Useful for displaying progress during navigation or animating route visualization.\n *\n * @remarks\n * Orbis v3 has no per-representation selector: progress points are all-or-nothing. Any\n * non-empty array therefore requests both `distance` and `travelTime`. Pass an empty array\n * to opt out entirely and keep the progress data out of the response payload.\n *\n * @default ['distance', 'travelTime']\n *\n * @example\n * ```typescript\n * extendedRouteRepresentations: ['distance', 'travelTime']\n *\n * // Opt out of progress points altogether\n * extendedRouteRepresentations: []\n * ```\n */\n extendedRouteRepresentations?: ExtendedRouteRepresentation[];\n\n /**\n * Request turn-by-turn guidance instructions.\n *\n * When specified, the response includes detailed instructions with maneuvers and road\n * names.\n *\n * @example\n * ```typescript\n * guidance: { type: 'coded' }\n * ```\n */\n guidance?: GuidanceParams;\n\n /**\n * Route section types to include in the response.\n *\n * Sections help you display route characteristics like tolls, ferries, or traffic.\n * Leg sections are always included regardless of this parameter. A section type is only\n * present in the response if the route actually traverses it. The `lanes` section is\n * guidance-only and is returned solely when {@link GuidanceParams | `guidance`} is also\n * requested.\n *\n * @default When omitted, all section types are requested.\n *\n * @example\n * ```typescript\n * // Request specific sections (any combination of available section types)\n * sectionTypes: ['toll', 'ferry', 'traffic', 'country', 'roadShields']\n *\n * // Request no optional sections\n * sectionTypes: []\n * ```\n */\n sectionTypes?: InputSectionTypes;\n\n /**\n * Maximum number of alternative routes to calculate.\n *\n * Alternative routes provide different travel options between the same origin and destination.\n * Each alternative is optimized differently (e.g., avoiding highways, minimizing tolls).\n *\n * @remarks\n * - More alternatives increase response time\n * - Alternatives are returned in order of quality (best first)\n * - Not all requests will return the maximum number of alternatives\n *\n * @default 0\n *\n * @example\n * ```typescript\n * // Request up to 2 alternatives\n * maxAlternatives: 2\n * ```\n */\n maxAlternatives?: MaxNumberOfAlternatives;\n };\n","import type { SectionType, Waypoint } from '@tomtom-org/maps-sdk/core';\nimport { getRoutePlanningLocationType, inputSectionTypesWithGuidance } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { predefinedModelIdOf } from '../shared/request/routingBodyBuilder';\nimport { commonRoutingRequestSchema } from '../shared/schema/commonRoutingRequestSchema';\nimport {\n featureSchema,\n geometrySchema,\n hasLngLatSchema,\n lineStringCoordsSchema,\n} from '../shared/schema/geometriesSchema';\nimport type { SchemaRefinement } from '../shared/types/validation';\nimport { isLDEVRRequest, PREDEFINED_MODEL_WITHOUT_LDEVR_MESSAGE } from './requestBuilder';\nimport { arrivalSides, type CalculateRouteParams, chargingStopsStrategies } from './types/calculateRouteParams';\n\nconst waypointLikeSchema = z.union([hasLngLatSchema, geometrySchema]);\nconst pathLikeSchema = z.union([lineStringCoordsSchema, featureSchema]);\n\nconst mandatorySchema = z.object({\n locations: z.array(z.union([waypointLikeSchema, pathLikeSchema])).min(1), // see calculateRouteLocationsRefinement\n});\n\nconst optionalSchema = z.object({\n arrivalSide: z.enum(arrivalSides).optional(),\n chargingStopsStrategy: z.enum(chargingStopsStrategies).optional(),\n computeTravelTimeFor: z.enum(['none', 'all']).optional(),\n maxAlternatives: z.number().min(0).max(5).optional(),\n sectionTypes: z.array(z.enum(inputSectionTypesWithGuidance as [SectionType, ...SectionType[]])).optional(),\n});\n\nconst schema = commonRoutingRequestSchema.extend(mandatorySchema.extend(optionalSchema.shape).shape);\n\nconst locationsRefinement: SchemaRefinement<CalculateRouteParams> = {\n check: (data: CalculateRouteParams): boolean => {\n const types = data.locations.map(getRoutePlanningLocationType);\n const waypointCount = types.filter((type) => type === 'waypoint').length;\n const pathCount = types.filter((type) => type === 'path').length;\n // At least 2 waypoints, OR at least 1 path (reconstruction)\n return waypointCount >= 2 || pathCount >= 1;\n },\n message: 'At least 2 waypoints or 1 path (route reconstruction) is required.',\n};\n\n// The strategy only reaches the wire on the LDEVR endpoint, and only charging preferences select\n// that endpoint. A strategy on its own would be dropped by the request builder, so ask the builder's\n// own predicate rather than re-deciding here: a `chargingPreferences: undefined` that satisfied a\n// key check would pass validation and then silently lose the strategy.\nconst chargingStopsStrategyRefinement: SchemaRefinement<CalculateRouteParams> = {\n check: (data: CalculateRouteParams): boolean => !data.chargingStopsStrategy || isLDEVRRequest(data),\n message:\n 'chargingStopsStrategy: set vehicle.preferences.chargingPreferences as well — an EV route needs a minimum charge at the destination, which only the charging preferences provide.',\n};\n\n// A variant identifies a vehicle in TomTom's database, which only the LDEVR endpoint looks up —\n// `/routes/calculate` answers `400` for it. The rule is the endpoint's, not the vehicle's:\n// `calculateReachableRange` accepts a variant with no charging preferences at all, so it cannot live\n// on the shared vehicle type.\nconst predefinedModelRefinement: SchemaRefinement<CalculateRouteParams> = {\n check: (data: CalculateRouteParams): boolean =>\n !(data.vehicle && predefinedModelIdOf(data.vehicle)) || isLDEVRRequest(data),\n message: PREDEFINED_MODEL_WITHOUT_LDEVR_MESSAGE,\n};\n\n// The routing API requires the last leg's wait to be 0, so a pause on the destination is rejected.\n// Which location is the destination is positional, and `locations` is an array rather than a tuple —\n// callers build it dynamically — so the type cannot carry this and validation does.\nconst destinationPauseRefinement: SchemaRefinement<CalculateRouteParams> = {\n check: (data: CalculateRouteParams): boolean => {\n const destination = data.locations.at(-1);\n if (!destination || getRoutePlanningLocationType(destination) !== 'waypoint') return true;\n\n // A waypoint and a route are both Point/LineString Features, so which one this is cannot be\n // narrowed structurally — the check above is what decides it.\n return !(destination as Waypoint).properties?.pauseDurationSeconds;\n },\n message:\n 'pauseDurationSeconds is not supported on the destination: the routing API requires the wait on the last leg to be 0.',\n};\n\n/**\n * @ignore\n */\nexport const routeRequestValidationConfig = {\n schema,\n refinements: [\n locationsRefinement,\n chargingStopsStrategyRefinement,\n predefinedModelRefinement,\n destinationPauseRefinement,\n ],\n};\n","import {\n type DelayMagnitude,\n type TrafficIncidentCategory,\n type TrafficIncidentRequestCategory,\n trafficIncidentRequestCategories,\n} from '@tomtom-org/maps-sdk/core';\nimport type { DelayMagnitudeAPI, IconCategoryAPI } from './types/apiTrafficResponseTypes';\n\n// Keyed on the request categories, so a new one fails to compile until it has a v2 value.\nconst iconCategoryByCategory: Record<TrafficIncidentRequestCategory, IconCategoryAPI> = {\n other: 'unknown',\n accident: 'accident',\n fog: 'fog',\n danger: 'dangerousConditions',\n rain: 'rain',\n frost: 'ice',\n jam: 'jam',\n 'lane-closed': 'laneClosed',\n 'road-closed': 'roadClosed',\n roadworks: 'roadWorks',\n wind: 'wind',\n flooding: 'flooding',\n 'broken-down-vehicle': 'brokenDownVehicle',\n};\n\n// `Map` lookups throughout, so an inherited key such as `constructor` misses instead of resolving to a function.\nconst iconCategoryLookup = new Map<string, IconCategoryAPI>(Object.entries(iconCategoryByCategory));\n\nconst categoryByIconCategory = new Map<string | undefined, TrafficIncidentCategory>(\n trafficIncidentRequestCategories.map((category) => [iconCategoryByCategory[category], category]),\n);\n\nconst delayMagnitudeByAPIMagnitude: Record<DelayMagnitudeAPI, DelayMagnitude> = {\n unknown: 'unknown',\n minor: 'minor',\n moderate: 'moderate',\n major: 'major',\n undefined: 'indefinite',\n};\n\nconst delayMagnitudeLookup = new Map<string | undefined, DelayMagnitude>(Object.entries(delayMagnitudeByAPIMagnitude));\n\n/**\n * Maps a request category to its Orbis Traffic v2 `iconCategories` value. A value that skipped schema\n * validation and has none is sent unchanged, for the API to reject.\n */\nexport const toIconCategory = (category: TrafficIncidentRequestCategory): string =>\n iconCategoryLookup.get(category) ?? category;\n\n/**\n * Maps an Orbis Traffic v2 `iconCategory` to the SDK category; a missing or undocumented value is `'other'`.\n */\nexport const toTrafficIncidentCategory = (iconCategory: string | undefined): TrafficIncidentCategory =>\n categoryByIconCategory.get(iconCategory) ?? 'other';\n\n/**\n * Maps an Orbis Traffic v2 delay magnitude to the SDK one; a missing or undocumented value is `'unknown'`.\n */\nexport const toDelayMagnitude = (apiMagnitude: string | undefined): DelayMagnitude =>\n delayMagnitudeLookup.get(apiMagnitude) ?? 'unknown';\n","import {\n BBox,\n bboxFromGeoJSON,\n type ChargingStop,\n type CountrySectionProps,\n type CurrentType,\n type DrivingSide,\n type Guidance,\n generateId,\n type ImportantRoadStretchProps,\n type Instruction,\n type InstructionText,\n type Landmark,\n type LaneDirection,\n type LaneSectionProps,\n type LegSectionProps,\n type LegSummary,\n type Maneuver,\n type ManeuverAngle,\n type ManeuverView,\n type PossibleLaneDirection,\n type PossibleLaneSeparator,\n type RoadInformation,\n type RoadInformationProperty,\n type RoadShield,\n type RoadShieldReference,\n type RoadShieldSectionProps,\n type RoundaboutType,\n type Route,\n type RouteSummary,\n type Routes,\n type SectionProps,\n type SectionsProps,\n type SideRoad,\n type SideRoadSide,\n type Signpost,\n type SpeedLimitSectionProps,\n type TollPaymentType,\n type TrafficIncidentCategory,\n type TrafficIncidentTEC,\n type TrafficSectionProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { LineString, Position } from 'geojson';\nimport { isNil, omit } from 'lodash-es';\nimport { toChargingSpeed } from '../shared/ev';\nimport { toIso3 } from '../shared/iso2ToIso3';\nimport { maxChargeKWHOf } from '../shared/request/vehicleEnergyParams';\nimport { toDelayMagnitude, toTrafficIncidentCategory } from '../shared/trafficIncidents';\nimport type {\n BasicSectionAPI,\n CalculateRouteResponseAPI,\n ChargingStopAPI,\n CountrySectionAPI,\n CurrentTypeAPI,\n ImportantRoadStretchSectionAPI,\n InstructionAPI,\n InstructionRoadInformationAPI,\n InstructionRoadShieldAPI,\n InstructionTextAPI,\n LanesSectionAPI,\n LegAPI,\n ManeuverViewAPI,\n ProgressPointAPI,\n RouteAPI,\n SideRoadAPI,\n SpeedLimitSectionAPI,\n SummaryAPI,\n TrafficSectionAPI,\n TravelModeSectionAPI,\n} from './types/apiResponseTypes';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\nconst toCurrentType = (apiCurrentType: CurrentTypeAPI): CurrentType | undefined => {\n switch (apiCurrentType) {\n case 'Direct_Current':\n return 'DC';\n case 'Alternating_Current_1_Phase':\n return 'AC1';\n case 'Alternating_Current_3_Phase':\n return 'AC3';\n default:\n return undefined;\n }\n};\n\nconst toChargingStop = (\n chargingInformationAtEndOfLeg: ChargingStopAPI,\n maxChargeKWH: number | undefined,\n): ChargingStop => {\n const chargingConnectionInfo = chargingInformationAtEndOfLeg.chargingConnectionInfo;\n const chargingParkLocation = chargingInformationAtEndOfLeg.chargingParkLocation;\n const coordinates = [chargingParkLocation.coordinate.longitude, chargingParkLocation.coordinate.latitude];\n\n const addressParts = [chargingParkLocation.street, chargingParkLocation.houseNumber].filter(Boolean);\n const freeformAddress = addressParts.length > 0 ? addressParts.join(', ') : '';\n\n return {\n type: 'Feature',\n id: chargingInformationAtEndOfLeg.chargingParkId,\n geometry: { type: 'Point', coordinates },\n properties: {\n ...omit(chargingInformationAtEndOfLeg, ['chargingConnectionInfo', 'chargingParkLocation']),\n type: 'POI',\n address: {\n freeformAddress,\n ...(chargingParkLocation.street && { streetName: chargingParkLocation.street }),\n ...(chargingParkLocation.houseNumber && { streetNumber: chargingParkLocation.houseNumber }),\n ...(chargingParkLocation.city && { municipality: chargingParkLocation.city }),\n ...(chargingParkLocation.region && { countrySubdivision: chargingParkLocation.region }),\n ...(chargingParkLocation.postalCode && { postalCode: chargingParkLocation.postalCode }),\n ...(chargingParkLocation.country && { country: chargingParkLocation.country }),\n },\n ...(chargingConnectionInfo && {\n chargingConnectionInfo: {\n plugType: chargingConnectionInfo.chargingPlugType,\n currentInA: chargingConnectionInfo.chargingCurrentInA,\n voltageInV: chargingConnectionInfo.chargingVoltageInV,\n chargingPowerInkW: chargingConnectionInfo.chargingPowerInkW,\n currentType: toCurrentType(chargingConnectionInfo.chargingCurrentType),\n chargingSpeed: toChargingSpeed(chargingConnectionInfo.chargingPowerInkW),\n },\n }),\n ...(maxChargeKWH && {\n targetChargeInPCT: (100 * chargingInformationAtEndOfLeg.targetChargeInkWh) / maxChargeKWH,\n }),\n ...(chargingInformationAtEndOfLeg.chargingParkPowerInkW && {\n chargingParkSpeed: toChargingSpeed(chargingInformationAtEndOfLeg.chargingParkPowerInkW),\n }),\n },\n };\n};\n\nconst parseSummary = (apiSummary: SummaryAPI, params: CalculateRouteParams): RouteSummary | LegSummary => {\n const maxChargeKWH = params?.vehicle && maxChargeKWHOf(params.vehicle);\n return {\n lengthInMeters: apiSummary.lengthInMeters,\n travelTimeInSeconds: apiSummary.travelDurationInSeconds,\n trafficDelayInSeconds: apiSummary.trafficDelayDurationInSeconds ?? 0,\n trafficLengthInMeters: apiSummary.trafficLengthInMeters ?? 0,\n historicTrafficTravelTimeInSeconds: apiSummary.historicTrafficTravelTimeInSeconds,\n liveTrafficIncidentsTravelTimeInSeconds: apiSummary.liveTrafficIncidentsTravelTimeInSeconds,\n noTrafficTravelTimeInSeconds: apiSummary.noTrafficTravelTimeInSeconds,\n departureTime: new Date(apiSummary.departureDateTime),\n arrivalTime: new Date(apiSummary.arrivalDateTime),\n deviationDistanceInMeters: apiSummary.deviationDistanceInMeters,\n deviationTimeInSeconds: apiSummary.deviationDurationInSeconds,\n ...(apiSummary.deviationPoint && {\n deviationPoint: apiSummary.deviationPoint.coordinates,\n }),\n fuelConsumptionInLiters: apiSummary.fuelConsumptionInLiters,\n totalChargingTimeInSeconds: apiSummary.totalChargingTimeInSeconds,\n batteryConsumptionInkWh: apiSummary.batteryConsumptionInKilowattHours ?? apiSummary.batteryConsumptionInkWh,\n ...(maxChargeKWH &&\n (() => {\n const batteryConsumption =\n apiSummary.batteryConsumptionInKilowattHours ?? apiSummary.batteryConsumptionInkWh;\n return batteryConsumption\n ? { batteryConsumptionInPCT: (100 * batteryConsumption) / maxChargeKWH }\n : undefined;\n })()),\n remainingChargeAtArrivalInkWh: apiSummary.remainingChargeAtArrivalInkWh,\n ...(maxChargeKWH &&\n apiSummary.remainingChargeAtArrivalInkWh && {\n remainingChargeAtArrivalInPCT: (100 * apiSummary.remainingChargeAtArrivalInkWh) / maxChargeKWH,\n }),\n ...(apiSummary.chargingInformationAtEndOfLeg && {\n chargingInformationAtEndOfLeg: toChargingStop(apiSummary.chargingInformationAtEndOfLeg, maxChargeKWH),\n }),\n };\n};\n\nconst parseRoutePath = (apiRouteLegs: LegAPI[]): LineString => ({\n type: 'LineString',\n coordinates: apiRouteLegs.flatMap((leg) => leg.path.coordinates),\n});\n\n// A stop is only visible as the gap between arriving on one leg and departing on the next, so it\n// takes the pair to see it. Everything spent there widens that one gap -- a requested wait, or\n// charging on an EV route, or both at one stop -- so the gap is reported as it is, and\n// `chargingInformationAtEndOfLeg` is left to say how much of it was charging.\nconst parseLegStopTime = (leg: LegAPI, nextLeg: LegAPI | undefined): number | undefined => {\n if (!nextLeg) return undefined;\n\n const arrival = new Date(leg.summary.arrivalDateTime).getTime();\n const nextDeparture = new Date(nextLeg.summary.departureDateTime).getTime();\n if (Number.isNaN(arrival) || Number.isNaN(nextDeparture)) return undefined;\n\n const stopSeconds = Math.round((nextDeparture - arrival) / 1000);\n return stopSeconds > 0 ? stopSeconds : undefined;\n};\n\nconst parseLegSectionProps = (apiLegs: LegAPI[], params: CalculateRouteParams): LegSectionProps[] =>\n apiLegs.reduce<LegSectionProps[]>((accumulator, leg, legIndex) => {\n const legCoordinateCount = leg.path.coordinates.length;\n const lastEndIndex = legIndex === 0 ? 0 : accumulator[legIndex - 1]?.endPointIndex;\n let endPointIndex: number | undefined;\n if (!isNil(lastEndIndex)) {\n endPointIndex =\n lastEndIndex === 0 ? Math.max(legCoordinateCount - 1, 0) : lastEndIndex + legCoordinateCount;\n }\n const stopTimeInSeconds = parseLegStopTime(leg, apiLegs[legIndex + 1]);\n // A leg is not reliably the one arriving at `locations[legIndex + 1]`, because the service\n // inserts charging stops of its own. It says which requested waypoint each leg ended at, so\n // that is what the caller gets to map a leg back with.\n const { originalWaypointIndexAtEndOfLeg } = leg.summary;\n accumulator.push({\n ...(!isNil(lastEndIndex) && { startPointIndex: lastEndIndex }),\n ...(endPointIndex !== undefined && { endPointIndex }),\n ...(!isNil(originalWaypointIndexAtEndOfLeg) && {\n originalWaypointIndex: originalWaypointIndexAtEndOfLeg,\n }),\n summary: {\n ...(parseSummary(leg.summary, params) as LegSummary),\n ...(stopTimeInSeconds !== undefined && { stopTimeInSeconds }),\n },\n id: generateId(),\n });\n return accumulator;\n }, []);\n\nconst toSectionProps = (section: BasicSectionAPI): SectionProps => ({\n id: generateId(),\n startPointIndex: section.startPathIndex,\n endPointIndex: section.endPathIndex,\n});\n\n/**\n * Map a traffic section to the SDK's semantic traffic incident categories.\n * Uses TEC main cause codes when available, falling back to the API's coarser `iconCategory`.\n * @ignore\n */\nexport const toTrafficCategories = (section: TrafficSectionAPI): TrafficIncidentCategory[] => {\n if (section.tec?.causes?.length) {\n return section.tec.causes.map((cause) => {\n switch (cause.mainCauseCode) {\n case 1:\n return 'jam';\n case 2:\n return 'accident';\n case 3:\n return 'roadworks';\n case 4:\n return 'narrow-lanes';\n case 5:\n return 'road-closed';\n case 9:\n return 'danger';\n case 11:\n return 'animals-on-road';\n case 13:\n return 'broken-down-vehicle';\n case 16:\n return 'lane-closed';\n case 17:\n return 'wind';\n case 18:\n return 'fog';\n case 19:\n return 'rain';\n case 22:\n return 'frost';\n case 23:\n return 'flooding';\n default:\n return 'other';\n }\n });\n }\n return [toTrafficIncidentCategory(section.iconCategory)];\n};\n\ntype BasicSectionKey =\n | 'carTrain'\n | 'ferry'\n | 'tunnel'\n | 'motorway'\n | 'pedestrian'\n | 'toll'\n | 'tollRoad'\n | 'carpool'\n | 'urban'\n | 'unpaved'\n | 'lowEmissionZone';\n\nconst parseSpeedLimitSections = (speedLimitSections: SpeedLimitSectionAPI[]): SpeedLimitSectionProps[] =>\n // Speeds arrive under `speedRestrictions`; the SDK exposes only the maximum limit.\n speedLimitSections\n .map((speedLimit): SpeedLimitSectionProps | undefined => {\n const max = speedLimit.speedRestrictions?.find((r) => r.type === 'maximum')?.inKilometersPerHour;\n return isNil(max) ? undefined : { ...toSectionProps(speedLimit), maxSpeedLimitInKmh: max };\n })\n .filter((speedLimit): speedLimit is SpeedLimitSectionProps => !!speedLimit);\n\nconst parseLaneSections = (laneSections: LanesSectionAPI[]): LaneSectionProps[] =>\n // Lane directions/separators are camelCase strings; map to the SDK's UPPER_SNAKE enums.\n laneSections.map(\n (lanesSection): LaneSectionProps => ({\n ...toSectionProps(lanesSection),\n lanes: (lanesSection.lanes ?? []).map(\n (lane): LaneDirection => ({\n directions: lane.directions.map(\n (direction) => LANE_DIRECTION_MAP[direction] ?? (direction as PossibleLaneDirection),\n ),\n ...(lane.follow && {\n follow: LANE_DIRECTION_MAP[lane.follow] ?? (lane.follow as PossibleLaneDirection),\n }),\n }),\n ),\n laneSeparators: (lanesSection.laneSeparators ?? []).map(\n (separator) => LANE_SEPARATOR_MAP[separator] ?? (separator as PossibleLaneSeparator),\n ),\n properties: lanesSection.properties,\n }),\n );\n\nconst parseImportantRoadStretchSections = (\n importantRoadStretchSections: ImportantRoadStretchSectionAPI[],\n): ImportantRoadStretchProps[] =>\n importantRoadStretchSections.map(\n (importantRoadStretch): ImportantRoadStretchProps => ({\n ...toSectionProps(importantRoadStretch),\n index: importantRoadStretch.importantRoadStretchIndex,\n ...(importantRoadStretch.streetName && { streetName: importantRoadStretch.streetName.text }),\n ...(importantRoadStretch.roadNumbers?.length && {\n roadNumbers: importantRoadStretch.roadNumbers.map((roadNumber) => roadNumber.text),\n }),\n }),\n );\n\n// The API always returns every section type when sections are requested. Restore the legacy filtering by\n// dropping the section keys the caller did not ask for (the always-present `leg` is kept).\nconst applyRequestedSectionFilter = (\n result: SectionsProps,\n sectionTypes: CalculateRouteParams['sectionTypes'],\n): void => {\n if (!sectionTypes) return;\n const requested = new Set<string>(sectionTypes);\n for (const key of Object.keys(result) as (keyof SectionsProps)[]) {\n if (key !== 'leg' && !requested.has(key)) {\n delete result[key];\n }\n }\n};\n\nconst parseSections = (apiRoute: RouteAPI, params: CalculateRouteParams): SectionsProps => {\n const result: SectionsProps = {\n leg: parseLegSectionProps(apiRoute.legs, params),\n };\n\n const sections = apiRoute.sections;\n if (!sections) return result;\n\n const addBasic = (key: BasicSectionKey, items?: BasicSectionAPI[]) => {\n if (items?.length) result[key] = items.map(toSectionProps);\n };\n\n addBasic('carTrain', sections.carTrain);\n addBasic('ferry', sections.ferry);\n addBasic('tunnel', sections.tunnel);\n addBasic('motorway', sections.motorway);\n addBasic('pedestrian', sections.pedestrian);\n addBasic('toll', sections.toll);\n addBasic('tollRoad', sections.tollRoad);\n addBasic('carpool', sections.carpool);\n addBasic('urban', sections.urban);\n addBasic('unpaved', sections.unpaved);\n addBasic('lowEmissionZone', sections.lowEmissionZone);\n\n if (sections.country?.length) {\n result.country = sections.country.map((country: CountrySectionAPI) => ({\n ...toSectionProps(country),\n countryCodeISO2: country.countryCodeIso2 ?? '',\n countryCodeISO3: toIso3(country.countryCodeIso2 ?? ''),\n }));\n }\n\n if (sections.travelMode?.length) {\n result.vehicleRestricted = sections.travelMode\n .filter((travelModeSection: TravelModeSectionAPI) => travelModeSection.travelMode === 'other')\n .map(toSectionProps);\n }\n\n if (sections.traffic?.length) {\n result.traffic = sections.traffic.map(\n (traffic: TrafficSectionAPI): TrafficSectionProps => ({\n ...toSectionProps(traffic),\n delayInSeconds: traffic.delayDurationInSeconds,\n effectiveSpeedInKmh: traffic.effectiveSpeedInKilometersPerHour,\n categories: toTrafficCategories(traffic),\n magnitudeOfDelay: toDelayMagnitude(traffic.delayMagnitude),\n tec: traffic.tec as TrafficIncidentTEC,\n ...(traffic.eventId && { eventId: traffic.eventId }),\n }),\n );\n }\n\n if (sections.speedLimit?.length) {\n result.speedLimit = parseSpeedLimitSections(sections.speedLimit);\n }\n\n if (sections.lanes?.length) {\n result.lanes = parseLaneSections(sections.lanes);\n }\n\n if (sections.roadShields?.length) {\n result.roadShields = sections.roadShields.map(\n (roadShield): RoadShieldSectionProps => ({\n ...toSectionProps(roadShield),\n roadShieldReferences: roadShield.roadShieldReferences as RoadShieldReference[],\n }),\n );\n }\n\n if (sections.tollVignette?.length) {\n result.tollVignette = sections.tollVignette.map(\n (tollVignette: CountrySectionAPI): CountrySectionProps => ({\n ...toSectionProps(tollVignette),\n countryCodeISO2: tollVignette.countryCodeIso2 ?? '',\n countryCodeISO3: toIso3(tollVignette.countryCodeIso2 ?? ''),\n }),\n );\n }\n\n if (sections.importantRoadStretch?.length) {\n result.importantRoadStretch = parseImportantRoadStretchSections(sections.importantRoadStretch);\n }\n\n applyRequestedSectionFilter(result, params.sectionTypes);\n\n return result;\n};\n\nconst DELTA = 0.0001;\nconst similar = (pointA: Position, pointB: Position): boolean =>\n Math.abs(pointA[0] - pointB[0]) < DELTA && Math.abs(pointA[1] - pointB[1]) < DELTA;\n\n// Maneuver camelCase JSON values → SDK UPPER_SNAKE_CASE Maneuver type.\n// The two renamed cases: continueStraight → STRAIGHT, passTollgate → TOLLGATE.\nconst MANEUVER_MAP: Record<string, Maneuver> = {\n depart: 'DEPART',\n waypointLeft: 'WAYPOINT_LEFT',\n waypointRight: 'WAYPOINT_RIGHT',\n waypointReached: 'WAYPOINT_REACHED',\n waypointAhead: 'WAYPOINT_AHEAD',\n arrive: 'ARRIVE',\n arriveLeft: 'ARRIVE_LEFT',\n arriveRight: 'ARRIVE_RIGHT',\n arriveAhead: 'ARRIVE_AHEAD',\n continueStraight: 'STRAIGHT',\n keepLeft: 'KEEP_LEFT',\n keepRight: 'KEEP_RIGHT',\n turnSlightRight: 'SLIGHT_RIGHT',\n turnRight: 'TURN_RIGHT',\n turnSharpRight: 'SHARP_RIGHT',\n turnSlightLeft: 'SLIGHT_LEFT',\n turnLeft: 'TURN_LEFT',\n turnSharpLeft: 'SHARP_LEFT',\n makeUTurn: 'MAKE_UTURN',\n roundaboutStraight: 'ROUNDABOUT_STRAIGHT',\n roundaboutSharpRight: 'ROUNDABOUT_SHARP_RIGHT',\n roundaboutRight: 'ROUNDABOUT_RIGHT',\n roundaboutSlightRight: 'ROUNDABOUT_SLIGHT_RIGHT',\n roundaboutSlightLeft: 'ROUNDABOUT_SLIGHT_LEFT',\n roundaboutLeft: 'ROUNDABOUT_LEFT',\n roundaboutSharpLeft: 'ROUNDABOUT_SHARP_LEFT',\n roundaboutBack: 'ROUNDABOUT_BACK',\n exitRoundabout: 'EXIT_ROUNDABOUT',\n exitMotorwayLeft: 'EXIT_MOTORWAY_LEFT',\n exitMotorwayRight: 'EXIT_MOTORWAY_RIGHT',\n switchMotorwayLeft: 'SWITCH_MOTORWAY_LEFT',\n switchMotorwayRight: 'SWITCH_MOTORWAY_RIGHT',\n enterCarpoolLaneLeft: 'ENTER_CARPOOL_LANE_LEFT',\n enterCarpoolLaneRight: 'ENTER_CARPOOL_LANE_RIGHT',\n exitCarpoolLaneLeft: 'EXIT_CARPOOL_LANE_LEFT',\n exitCarpoolLaneRight: 'EXIT_CARPOOL_LANE_RIGHT',\n mergeLeftLane: 'MERGE_LEFT_LANE',\n mergeRightLane: 'MERGE_RIGHT_LANE',\n takeShipFerry: 'TAKE_SHIP_FERRY',\n takeCarTrain: 'TAKE_CAR_TRAIN',\n leaveShipFerry: 'LEAVE_SHIP_FERRY',\n leaveCarTrain: 'LEAVE_CAR_TRAIN',\n crossBorder: 'CROSS_BORDER',\n passTollgate: 'TOLLGATE',\n};\n\nconst ROAD_PROPERTY_MAP: Record<string, RoadInformationProperty> = {\n urban: 'URBAN',\n motorway: 'MOTORWAY',\n controlledAccess: 'CONTROLLED_ACCESS',\n};\n\n// Landmark camelCase JSON values → SDK UPPER_SNAKE_CASE Landmark type.\nconst LANDMARK_MAP: Record<string, Landmark> = {\n endOfRoad: 'END_OF_ROAD',\n atTrafficLight: 'AT_TRAFFIC_LIGHT',\n onToBridge: 'ON_TO_BRIDGE',\n onBridge: 'ON_BRIDGE',\n afterBridge: 'AFTER_BRIDGE',\n intoTunnel: 'INTO_TUNNEL',\n insideTunnel: 'INSIDE_TUNNEL',\n afterTunnel: 'AFTER_TUNNEL',\n};\n\n// Toll payment camelCase JSON values → SDK UPPER_SNAKE_CASE TollPaymentType.\nconst TOLL_PAYMENT_MAP: Record<string, TollPaymentType> = {\n cashCoinsAndBills: 'CASH_COINS_AND_BILLS',\n cashBillsOnly: 'CASH_BILLS_ONLY',\n cashCoinsOnly: 'CASH_COINS_ONLY',\n cashExactChange: 'CASH_EXACT_CHANGE',\n creditCard: 'CREDIT_CARD',\n debitCard: 'DEBIT_CARD',\n travelCard: 'TRAVEL_CARD',\n etc: 'ETC',\n etcTransponder: 'ETC_TRANSPONDER',\n etcVideoCamera: 'ETC_VIDEO_CAMERA',\n subscription: 'SUBSCRIPTION',\n};\n\n// Lane direction camelCase JSON values → SDK UPPER_SNAKE_CASE PossibleLaneDirection.\nconst LANE_DIRECTION_MAP: Record<string, PossibleLaneDirection> = {\n straight: 'STRAIGHT',\n slightRight: 'SLIGHT_RIGHT',\n right: 'RIGHT',\n sharpRight: 'SHARP_RIGHT',\n rightUTurn: 'RIGHT_U_TURN',\n slightLeft: 'SLIGHT_LEFT',\n left: 'LEFT',\n sharpLeft: 'SHARP_LEFT',\n leftUTurn: 'LEFT_U_TURN',\n};\n\n// Lane separator camelCase JSON values → SDK UPPER_SNAKE_CASE PossibleLaneSeparator.\nconst LANE_SEPARATOR_MAP: Record<string, PossibleLaneSeparator> = {\n unknown: 'UNKNOWN',\n noMarking: 'NO_MARKING',\n longDashed: 'LONG_DASHED',\n doubleSolid: 'DOUBLE_SOLID',\n singleSolid: 'SINGLE_SOLID',\n solidDashed: 'SOLID_DASHED',\n dashedSolid: 'DASHED_SOLID',\n shortDashed: 'SHORT_DASHED',\n shadedAreaMarking: 'SHADED_AREA_MARKING',\n dashedBlocks: 'DASHED_BLOCKS',\n doubleDashed: 'DOUBLE_DASHED',\n crossingAlert: 'CROSSING_ALERT',\n physicalDivider: 'PHYSICAL_DIVIDER',\n physicalDividerLessThan3m: 'PHYSICAL_DIVIDER_LESS_THAN_3M',\n physicalDividerGuardrail: 'PHYSICAL_DIVIDER_GUARDRAIL',\n curb: 'CURB',\n};\n\n// Maneuver-view angles are relative-direction words, not degrees → SDK ManeuverAngle.\nconst MANEUVER_ANGLE_MAP: Record<string, ManeuverAngle> = {\n straight: 'STRAIGHT',\n slightRight: 'SLIGHT_RIGHT',\n right: 'RIGHT',\n sharpRight: 'SHARP_RIGHT',\n slightLeft: 'SLIGHT_LEFT',\n left: 'LEFT',\n sharpLeft: 'SHARP_LEFT',\n back: 'BACK',\n};\n\n// Roundabout kinds → SDK RoundaboutType.\nconst ROUNDABOUT_TYPE_MAP: Record<string, RoundaboutType> = {\n regular: 'REGULAR',\n small: 'SMALL',\n};\n\n// Side-road sides are lowercase on the wire → SDK SideRoadSide.\nconst SIDE_ROAD_SIDE_MAP: Record<string, SideRoadSide> = {\n left: 'LEFT',\n right: 'RIGHT',\n leftAndRight: 'LEFT_AND_RIGHT',\n};\n\nconst EMPTY_ROAD_INFO: RoadInformation = { properties: [] };\n\nconst toInstructionText = (instructionText: InstructionTextAPI): InstructionText => ({ text: instructionText.text });\n\nconst parseInstructionRoadInformation = (road: InstructionRoadInformationAPI): RoadInformation => ({\n properties: (road.properties ?? []).map(\n (p: string) => ROAD_PROPERTY_MAP[p] ?? (p.toUpperCase() as RoadInformationProperty),\n ),\n ...(road.roadNames?.length && { streetName: toInstructionText(road.roadNames[0].identifier) }),\n ...(road.countryCodeIso2 && { countryCode: toIso3(road.countryCodeIso2) }),\n ...(road.roadShields?.length && {\n roadShields: road.roadShields.map(\n (s: InstructionRoadShieldAPI): RoadShield => ({\n roadNumber: s.roadNumber ? toInstructionText(s.roadNumber) : { text: '' },\n countryCode: toIso3(s.countryCodeIso2 ?? ''),\n ...(s.countrySubdivisionCodeIso && { stateCode: s.countrySubdivisionCodeIso }),\n ...(s.iconReference && { roadShieldReference: s.iconReference }),\n }),\n ),\n }),\n});\n\nconst parseSignpost = (signpost: NonNullable<InstructionAPI['signpost']>): Signpost | undefined => {\n if (!signpost.exitName && !signpost.exitNumber && !signpost.towardName) return undefined;\n return {\n ...(signpost.exitName && { exitName: toInstructionText(signpost.exitName) }),\n ...(signpost.exitNumber && { exitNumber: toInstructionText(signpost.exitNumber) }),\n // towardName is required on the SDK type; default to empty text when the API omits it.\n towardName: signpost.towardName ? toInstructionText(signpost.towardName) : { text: '' },\n };\n};\n\n// Tollgate + border-crossing fields, only present on the relevant maneuvers (kept separate to keep\n// the main instruction builder's complexity in check).\nconst parseTollAndCrossing = (a: InstructionAPI): Partial<Instruction> => ({\n ...(a.tollgateName && { tollgateName: toInstructionText(a.tollgateName) }),\n ...(a.tollPaymentTypes?.length && {\n tollPaymentTypes: a.tollPaymentTypes.map((t) => TOLL_PAYMENT_MAP[t] ?? (t as TollPaymentType)),\n }),\n ...(a.countryCrossingFromName && { countryCrossingFromName: toInstructionText(a.countryCrossingFromName) }),\n ...(a.countryCrossingFromCodeIso2 && { countryCrossingFromCode: toIso3(a.countryCrossingFromCodeIso2) }),\n ...(a.countryCrossingToName && { countryCrossingToName: toInstructionText(a.countryCrossingToName) }),\n ...(a.countryCrossingToCodeIso2 && { countryCrossingToCode: toIso3(a.countryCrossingToCodeIso2) }),\n});\n\n// Optional maneuver detail fields, only present on the relevant maneuver kinds.\nconst toManeuverAngle = (angle: string): ManeuverAngle => MANEUVER_ANGLE_MAP[angle] ?? (angle as ManeuverAngle);\n\n// The API side is lowercase and carries `isDrivable`; both need mapping onto the SDK's SideRoad.\nconst parseSideRoads = (sideRoads: SideRoadAPI[]): SideRoad[] =>\n sideRoads.map((sideRoad) => ({\n side: SIDE_ROAD_SIDE_MAP[sideRoad.side] ?? (sideRoad.side as SideRoadSide),\n offsetFromManeuverInMeters: sideRoad.offsetFromManeuverInMeters,\n ...(!isNil(sideRoad.isDrivable) && { isDrivable: sideRoad.isDrivable }),\n }));\n\nconst parseManeuverView = (view: ManeuverViewAPI): ManeuverView => ({\n ...(view.onRouteAngle && { onRouteAngle: toManeuverAngle(view.onRouteAngle) }),\n offRouteAngles: (view.offRouteAngles ?? []).map(toManeuverAngle),\n});\n\nconst parseManeuverExtras = (a: InstructionAPI): Partial<Instruction> => ({\n ...(a.message && { message: a.message }),\n ...(a.intersectionName && { intersectionName: toInstructionText(a.intersectionName) }),\n ...(a.drivingSide && { drivingSide: a.drivingSide.toUpperCase() as DrivingSide }),\n ...(a.landmark && { landmark: LANDMARK_MAP[a.landmark] ?? (a.landmark as Landmark) }),\n ...(!isNil(a.distanceToPreviousTrafficLightInMeters) && {\n trafficLightOffsetInMeters: a.distanceToPreviousTrafficLightInMeters,\n }),\n ...(a.sideRoads && { sideRoads: parseSideRoads(a.sideRoads) }),\n ...(a.maneuverView && { maneuverView: parseManeuverView(a.maneuverView) }),\n ...(a.roundaboutType && {\n roundaboutType: ROUNDABOUT_TYPE_MAP[a.roundaboutType] ?? (a.roundaboutType as RoundaboutType),\n }),\n ...(!isNil(a.isManeuverObligatory) && { isManeuverObligatory: a.isManeuverObligatory }),\n ...(!isNil(a.changeOfAngleInDegrees) && { changeOfAngleInDegrees: a.changeOfAngleInDegrees }),\n ...(!isNil(a.ambiguousExitOffsetFromManeuverInMeters) && {\n offsetOfAmbiguousExitFromManeuverInMeters: a.ambiguousExitOffsetFromManeuverInMeters,\n }),\n ...(!isNil(a.roundaboutExitNumber) && { roundaboutExitNumber: a.roundaboutExitNumber }),\n});\n\n// Road-shield related fields: signpost, the atlas base URL, and the flat reference lists. V2 exposed\n// a flat per-instruction road-shield list; the API carries them under nextRoadInformation.\nconst parseRoadShieldFields = (a: InstructionAPI, roadShieldAtlasReference?: string): Partial<Instruction> => {\n const signpost = a.signpost ? parseSignpost(a.signpost) : undefined;\n const roadShieldReferences = (a.nextRoadInformation?.roadShields ?? [])\n .map((s) => s.iconReference)\n .filter((ref): ref is RoadShieldReference => !!ref);\n return {\n ...(signpost && { signpost }),\n ...(roadShieldAtlasReference && { roadShieldAtlasReference }),\n ...(roadShieldReferences.length && { roadShieldReferences }),\n ...(a.signpost?.exitIconReference && {\n signpostRoadShieldReferences: [a.signpost.exitIconReference],\n }),\n };\n};\n\nconst parseInstruction = (\n a: InstructionAPI,\n maneuverPoint: [number, number],\n pathPointIndex: number,\n roadShieldAtlasReference?: string,\n): Instruction => ({\n maneuverPoint,\n pathPointIndex,\n routeOffsetInMeters: a.routeOffsetInMeters,\n maneuver: (a.maneuver ? (MANEUVER_MAP[a.maneuver] ?? a.maneuver) : a.maneuver) as Maneuver,\n routePath: (a.routePath ?? []).map((rp) => ({\n point: rp.point.coordinates,\n distanceInMeters: rp.distanceFromRouteStartInMeters,\n travelTimeInSeconds: rp.travelTimeFromRouteStartInSeconds,\n })),\n nextRoadInfo: a.nextRoadInformation ? parseInstructionRoadInformation(a.nextRoadInformation) : EMPTY_ROAD_INFO,\n previousRoadInfo: a.previousRoadInformation\n ? parseInstructionRoadInformation(a.previousRoadInformation)\n : EMPTY_ROAD_INFO,\n ...parseManeuverExtras(a),\n ...parseRoadShieldFields(a, roadShieldAtlasReference),\n ...parseTollAndCrossing(a),\n});\n\nconst parseGuidance = (\n instructions: InstructionAPI[],\n path: Position[],\n roadShieldAtlasReference?: string,\n): Guidance => {\n const parsed: Instruction[] = [];\n let lastPathIndex = 0;\n\n for (const apiInstruction of instructions) {\n const maneuverPoint = apiInstruction.maneuverPoint.coordinates;\n\n for (let pathIndex = lastPathIndex; pathIndex < path.length; pathIndex++) {\n if (similar(path[pathIndex], maneuverPoint)) {\n lastPathIndex = pathIndex;\n break;\n }\n }\n\n parsed.push(parseInstruction(apiInstruction, maneuverPoint, lastPathIndex, roadShieldAtlasReference));\n }\n\n return { instructions: parsed };\n};\n\nconst parseRoute = (\n apiRoute: RouteAPI,\n index: number,\n params: CalculateRouteParams,\n roadShieldAtlasReference?: string,\n): Route => {\n const geometry = parseRoutePath(apiRoute.legs);\n return {\n type: 'Feature',\n geometry,\n id: generateId(),\n bbox: bboxFromGeoJSON(geometry) as BBox,\n properties: {\n index,\n summary: parseSummary(apiRoute.summary, params),\n sections: parseSections(apiRoute, params),\n ...(apiRoute.instructions?.length && {\n guidance: parseGuidance(apiRoute.instructions, geometry.coordinates, roadShieldAtlasReference),\n }),\n ...(apiRoute.progressPoints && {\n progress: apiRoute.progressPoints.map((progressPoint: ProgressPointAPI) => ({\n pointIndex: progressPoint.pathIndex,\n distanceInMeters: progressPoint.distanceInMeters,\n travelTimeInSeconds: progressPoint.travelDurationInSeconds,\n })),\n }),\n },\n };\n};\n\n/**\n * Default method for parsing calculate route response from {@link CalculateRouteResponseAPI}\n * @param apiResponse The Routing API response.\n * @param params The params used to calculate this route.\n */\nexport const parseCalculateRouteResponse = (\n apiResponse: CalculateRouteResponseAPI,\n params: CalculateRouteParams,\n): Routes => {\n const features = apiResponse.routes.map((apiRoute, index) =>\n parseRoute(apiRoute, index, params, apiResponse.roadShieldAtlasReference),\n );\n const bbox = bboxFromGeoJSON(features);\n return { type: 'FeatureCollection', ...(bbox && { bbox }), features };\n};\n","import type { Routes } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { routeRequestValidationConfig } from './calculateRouteRequestSchema';\nimport { buildCalculateRouteRequest } from './requestBuilder';\nimport { parseCalculateRouteResponse } from './responseParser';\nimport { parseRoutingResponseError } from './routingResponseErrorParser';\nimport type { CalculateRoutePOSTDataAPI } from './types/apiRequestTypes';\nimport type { CalculateRouteResponseAPI } from './types/apiResponseTypes';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\n/**\n * @ignore\n */\nexport type CalculateRouteTemplate = ServiceTemplate<\n CalculateRouteParams,\n FetchInput<CalculateRoutePOSTDataAPI>,\n CalculateRouteResponseAPI,\n Routes\n>;\n\n/**\n * @ignore\n */\nexport const calculateRouteTemplate: CalculateRouteTemplate = {\n requestValidation: routeRequestValidationConfig,\n buildRequest: buildCalculateRouteRequest,\n sendRequest: fetchWith,\n parseResponse: parseCalculateRouteResponse,\n parseResponseError: parseRoutingResponseError,\n getAPIVersion: () => 3,\n};\n","import type { Routes } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { calculateRouteTemplate } from './calculateRouteTemplate';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\n/**\n * Calculates a route between an origin and destination, optionally passing through extra waypoints.\n *\n * @param params - Route calculation parameters including locations, travel mode, and other options\n * @returns Promise resolving to calculated route(s) with geometry, distance, and travel time\n *\n * @see [Calculate Route API Documentation](https://docs.tomtom.com/routing-api/documentation/tomtom-maps/calculate-route)\n *\n * @group Routing\n */\nexport const calculateRoute = async (params: CalculateRouteParams): Promise<Routes> =>\n callService(params, calculateRouteTemplate, 'Routing');\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { commonGeocodeAndFuzzySearchParamsSchema } from '../shared/schema/commonGeocodeAndFuzzySearchParamsSchema';\n\nconst fuzzySearchRequestOptional = z.object({\n minFuzzyLevel: z.number().min(1).max(4).optional(),\n maxFuzzyLevel: z.number().min(1).max(4).optional(),\n});\n\n/**\n * @ignore\n */\nexport const fuzzySearchRequestSchema = commonSearchParamsSchema.extend(\n commonGeocodeAndFuzzySearchParamsSchema.extend(fuzzySearchRequestOptional.shape).shape,\n);\n","import type { GetObject } from '../shared';\nimport { appendCommonSearchParams } from '../shared/request/commonSearchRequestBuilder';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport {\n appendByJoiningParamValue,\n appendGeoBiasParams,\n appendOptionalParam,\n buildPlacesRequestHeaders,\n} from '../shared/request/requestBuildingUtils';\nimport type { FuzzySearchParams } from './types';\n\nconst buildUrlBasePath = (params: FuzzySearchParams): string =>\n resolvePlacesEndpointUrl(params, `search/${encodeURIComponent(params.query ?? '')}.json`);\n\n/**\n * Default function for building a fuzzy search request from {@link FuzzySearchParams}\n * @param params The fuzzy search parameters, with global configuration already merged into them.\n */\nexport const buildFuzzySearchRequest = (params: FuzzySearchParams): GetObject => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n appendCommonSearchParams(url, params);\n const urlParams = url.searchParams;\n appendOptionalParam(urlParams, 'typeahead', params.typeahead);\n appendOptionalParam(urlParams, 'ofs', params.offset);\n appendByJoiningParamValue(urlParams, 'countrySet', params.countries);\n appendGeoBiasParams(urlParams, params.geoBias);\n appendOptionalParam(urlParams, 'minFuzzyLevel', params.minFuzzyLevel);\n appendOptionalParam(urlParams, 'maxFuzzyLevel', params.maxFuzzyLevel);\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { latLonAPIToPosition } from '../shared/geometry';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { FuzzySearchResponse, FuzzySearchResponseAPI, QueryIntent, QueryIntentAPI } from './types';\n\nconst queryIntentApiToSdk = (intentApi: QueryIntentAPI): QueryIntent => {\n let intent;\n switch (intentApi.type) {\n case 'COORDINATE':\n intent = { ...intentApi, details: { position: latLonAPIToPosition(intentApi.details) } };\n break;\n case 'NEARBY':\n intent = {\n ...intentApi,\n details: {\n position: latLonAPIToPosition({ lon: intentApi.details.lon, lat: intentApi.details.lat }),\n text: intentApi.details.text,\n query: intentApi.details.query,\n },\n };\n break;\n case 'BOOKMARK':\n case 'W3W':\n intent = intentApi;\n }\n return intent;\n};\n\n/**\n * Default function to parse a fuzzy search response.\n * @param apiResponse The API response.\n */\nexport const parseFuzzySearchResponse = (apiResponse: FuzzySearchResponseAPI): FuzzySearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n const queryIntent = apiResponse.summary.queryIntent?.map(queryIntentApiToSdk);\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n ...(queryIntent && { queryIntent }),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { fuzzySearchRequestSchema } from './fuzzySearchRequestSchema';\nimport { buildFuzzySearchRequest } from './requestBuilder';\nimport { parseFuzzySearchResponse } from './responseParser';\nimport type { FuzzySearchParams, FuzzySearchResponse, FuzzySearchResponseAPI } from './types';\n\n/**\n * Fuzzy search service template type.\n * @ignore\n */\nexport type FuzzySearchTemplate = ServiceTemplate<\n FuzzySearchParams,\n GetObject,\n FuzzySearchResponseAPI,\n FuzzySearchResponse\n>;\n\n/**\n * Fuzzy search service template main implementation.\n * @ignore\n */\nexport const fuzzySearchTemplate: FuzzySearchTemplate = {\n requestValidation: { schema: fuzzySearchRequestSchema },\n buildRequest: buildFuzzySearchRequest,\n sendRequest: get,\n parseResponse: parseFuzzySearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport { fuzzySearchTemplate } from './fuzzySearchTemplate';\nimport type { FuzzySearchParams, FuzzySearchResponse } from './types';\n\n/**\n * Search for places using free-text queries with fuzzy matching.\n *\n * The Fuzzy Search service provides a flexible search that handles typos, abbreviations,\n * and incomplete addresses. It searches across POIs (Points of Interest), addresses,\n * and geographic areas to find the best matches for your query.\n *\n * @remarks\n * Key features:\n * - **Typo tolerance**: Handles misspellings and typing errors\n * - **Partial matching**: Works with incomplete queries\n * - **Multi-category search**: Searches POIs, addresses, and places simultaneously\n * - **Position bias**: Prioritizes results near a given location\n * - **Flexible input**: Accepts natural language queries\n *\n * The service is ideal for:\n * - User-facing search boxes where typos are common\n * - Location lookup without knowing exact names\n * - General \"find anything\" search functionality\n * - Autocomplete with final selection\n *\n * @param params Fuzzy search parameters including the search query\n *\n * @returns Promise resolving to a collection of matching places\n *\n * @example\n * ```typescript\n * // Basic search with typo tolerance\n * const results = await fuzzySearch({\n * query: 'amstrdam' // Typo: missing 'e'\n * });\n * // Still finds \"Amsterdam\"\n *\n * // Search near a specific location\n * const nearby = await fuzzySearch({\n * query: 'pizza',\n * position: [4.9041, 52.3676], // Amsterdam\n * radiusMeters: 2000, // Within 2km\n * limit: 10\n * });\n *\n * // Search with category filter\n * const restaurants = await fuzzySearch({\n * poiCategories: ['ITALIAN_RESTAURANT'],\n * position: [4.9041, 52.3676]\n * });\n *\n * // Partial address search restricted to a country\n * const addresses = await fuzzySearch({\n * query: '123 main st',\n * countries: ['US'],\n * limit: 5\n * });\n * ```\n *\n * @see [Fuzzy Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/fuzzy-search)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @ignore (exposed via 'search')\n */\nexport const fuzzySearch = async (params: FuzzySearchParams): Promise<FuzzySearchResponse> =>\n callService(params, fuzzySearchTemplate, 'FuzzySearch');\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { featureCollectionSchema, geometrySchema } from '../shared/schema/geometriesSchema';\n\nconst geometrySearchRequestMandatory = z.object({\n geometries: z.array(z.union([featureCollectionSchema, geometrySchema])),\n});\n\n/**\n * @ignore\n */\nexport const geometrySearchRequestSchema = commonSearchParamsSchema.extend(geometrySearchRequestMandatory.shape);\n","import { bboxFromCoordsArray } from '@tomtom-org/maps-sdk/core';\nimport type { MultiPolygon, Position } from 'geojson';\nimport type { PostObject } from '../shared';\nimport { sampleWithinMaxLength } from '../shared/arrays';\nimport { positionToCSVLatLon } from '../shared/geometry';\nimport { appendCommonSearchParams } from '../shared/request/commonSearchRequestBuilder';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { buildPlacesRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { GeometryAPI, GeometrySearchParams, GeometrySearchPayloadAPI, SearchGeometryInput } from './types';\n\nconst findFiftyLargestPolygons = (searchGeometry: MultiPolygon): Position[][][] => {\n // we calculate the size of each polygon based on bounding box (simplified)\n // we put it into a map for easy sorting\n // we sort the map by using size and keep only 50 largest polygons\n let polygonSizeMap = new Map<Position[][], number>();\n searchGeometry.coordinates.forEach((polygon) => {\n const bboxOfPolygon = bboxFromCoordsArray(polygon[0]);\n if (bboxOfPolygon) {\n const polygonSize = Math.abs((bboxOfPolygon[2] - bboxOfPolygon[0]) * (bboxOfPolygon[3] - bboxOfPolygon[1]));\n polygonSizeMap.set(polygon, polygonSize);\n }\n });\n polygonSizeMap = new Map([...polygonSizeMap.entries()].sort((a, b) => b[1] - a[1]).splice(0, 50));\n return [...polygonSizeMap.keys()];\n};\n\nconst sdkGeometryToApiGeometries = (searchGeometry: SearchGeometryInput): GeometryAPI[] => {\n switch (searchGeometry.type) {\n case 'Circle':\n return [\n {\n type: 'CIRCLE',\n radius: searchGeometry.radius,\n position: positionToCSVLatLon(searchGeometry.coordinates),\n },\n ];\n case 'Polygon':\n return [\n {\n type: 'POLYGON',\n vertices: sampleWithinMaxLength(searchGeometry.coordinates[0], 50).map((coord) =>\n positionToCSVLatLon(coord),\n ),\n },\n ];\n case 'MultiPolygon': {\n if (searchGeometry.coordinates.length > 50) {\n // we have too many polygons for the service to work\n return findFiftyLargestPolygons(searchGeometry).flatMap((polygonCoords) =>\n sdkGeometryToApiGeometries({ type: 'Polygon', coordinates: polygonCoords }),\n );\n }\n return searchGeometry.coordinates.flatMap((polygonCoords) =>\n sdkGeometryToApiGeometries({ type: 'Polygon', coordinates: polygonCoords }),\n );\n }\n case 'FeatureCollection':\n return searchGeometry.features.flatMap((feature) => sdkGeometryToApiGeometries(feature.geometry));\n default:\n // @ts-ignore\n throw new Error(`Type ${(searchGeometry as unknown).type} is not supported`);\n }\n};\n\nconst buildUrlBasePath = (mergedOptions: GeometrySearchParams): string =>\n resolvePlacesEndpointUrl(mergedOptions, `geometrySearch/${encodeURIComponent(mergedOptions.query ?? '')}.json`);\n\n/**\n * Default function for building a geometry search request from {@link GeometrySearchParams}\n * @param params The geometry search parameters, with global configuration already merged into them.\n */\nexport const buildGeometrySearchRequest = (params: GeometrySearchParams): PostObject<GeometrySearchPayloadAPI> => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n appendCommonSearchParams(url, params);\n\n return {\n url,\n headers: buildPlacesRequestHeaders(params),\n data: {\n geometryList: params.geometries.flatMap(sdkGeometryToApiGeometries),\n },\n };\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { GeometrySearchResponse, GeometrySearchResponseAPI } from './types';\n\n/**\n * Default function to parse a geometry search response.\n * @param apiResponse The API response.\n */\nexport const parseGeometrySearchResponse = (apiResponse: GeometrySearchResponseAPI): GeometrySearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { post } from '../shared/fetch';\nimport { geometrySearchRequestSchema } from './geometrySearchRequestSchema';\nimport { buildGeometrySearchRequest } from './requestBuilder';\nimport { parseGeometrySearchResponse } from './responseParser';\nimport type {\n GeometrySearchParams,\n GeometrySearchRequestAPI,\n GeometrySearchResponse,\n GeometrySearchResponseAPI,\n} from './types';\n\n/**\n * Geometry search service template type.\n * @ignore\n */\nexport type GeometrySearchTemplate = ServiceTemplate<\n GeometrySearchParams,\n GeometrySearchRequestAPI,\n GeometrySearchResponseAPI,\n GeometrySearchResponse\n>;\n\n/**\n * Geometry search service template main implementation.\n * @ignore\n */\nexport const geometrySearchTemplate: GeometrySearchTemplate = {\n requestValidation: { schema: geometrySearchRequestSchema },\n buildRequest: buildGeometrySearchRequest,\n sendRequest: post,\n parseResponse: parseGeometrySearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport { geometrySearchTemplate } from './geometrySearchTemplate';\nimport type { GeometrySearchParams, GeometrySearchResponse } from './types';\n\n/**\n * Search for places within specific geographic boundaries.\n *\n * The Geometry Search service finds places that fall within one or more defined areas,\n * such as polygons, circles, or corridors. This is essential for location-based filtering\n * and spatial queries where you need results constrained to specific regions.\n *\n * @remarks\n * Key features:\n * - **Area-based search**: Find places within polygons, circles, or along routes\n * - **Multiple geometries**: Search across several areas simultaneously\n * - **Precise boundaries**: Only returns results within the specified areas\n * - **Combined with text**: Filter by query text within the geometric boundaries\n * - **Category filtering**: Narrow results by POI categories\n *\n * Common use cases:\n * - **Route-based search**: \"Find gas stations along my route\"\n * - **Area filtering**: \"Restaurants within this neighborhood polygon\"\n * - **Corridor search**: \"Hotels within 5km of the highway\"\n * - **Service areas**: \"Stores within our delivery zone\"\n * - **Geofencing**: Places within administrative or custom boundaries\n *\n * @param params Geometry search parameters including geometries and optional query\n *\n * @returns Promise resolving to places within the specified geometries\n *\n * @example\n * ```typescript\n * // Search within a polygon (neighborhood boundaries)\n * const inArea = await geometrySearch({\n * query: 'coffee shop',\n * geometries: [{\n * type: 'Polygon',\n * coordinates: [[\n * [4.88, 52.36],\n * [4.90, 52.36],\n * [4.90, 52.38],\n * [4.88, 52.38],\n * [4.88, 52.36]\n * ]]\n * }]\n * });\n *\n * // Find POIs along a route (pass the route LineString as geometry)\n * const alongRoute = await geometrySearch({\n * query: 'gas station',\n * geometries: [routeLineString], // LineString from calculateRoute result\n * limit: 10\n * });\n *\n * // Search multiple areas at once\n * const multiArea = await geometrySearch({\n * query: 'pharmacy',\n * geometries: [polygonA, polygonB, polygonC],\n * limit: 20\n * });\n *\n * // Category search within geometry\n * const restaurants = await geometrySearch({\n * poiCategories: ['ITALIAN_RESTAURANT'],\n * geometries: [cityBoundary]\n * });\n * ```\n *\n * @see [Geometry Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/geometry-search)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @ignore (exposed via 'search')\n */\nexport const geometrySearch = async (params: GeometrySearchParams): Promise<GeometrySearchResponse> =>\n callService(params, geometrySearchTemplate, 'GeometrySearch');\n","import type { Place, Places, SearchPlaceProps } from '@tomtom-org/maps-sdk/core';\nimport type { AlongRouteSearchParams } from '../along-route-search';\nimport { alongRouteSearch } from '../along-route-search/alongRouteSearch';\nimport type { FuzzySearchParams, QueryIntent } from '../fuzzy-search';\nimport { fuzzySearch } from '../fuzzy-search/fuzzySearch';\nimport type { GeometrySearchParams } from '../geometry-search';\nimport { geometrySearch } from '../geometry-search/geometrySearch';\nimport type { SearchSummary } from '../shared';\nimport { fetchFirstPlace } from '../shared/firstPlace';\n\ntype SearchFeatureCollectionProps = SearchSummary & {\n queryIntent?: QueryIntent[];\n};\n\n/**\n * Search service response containing places that match the query.\n *\n * Collection of place features with search-specific properties like relevance scores and distances.\n *\n * @group Places Search\n */\nexport type DiscoverPlacesResponse = Places<SearchPlaceProps, SearchFeatureCollectionProps>;\n\n/**\n * Universal search function for finding places by text query or within geometries.\n *\n * This is a unified interface that automatically routes to either:\n * - **Geometry Search**: When geometries parameter is provided (search within specific areas)\n * - **Fuzzy Search**: When no geometries provided (free-text search)\n *\n * @remarks\n * The search service provides:\n * - POI (Points of Interest) search\n * - Address search\n * - Geographic area search\n * - Category-based filtering\n * - Position-based relevance ranking\n *\n * Results are ranked by relevance with scores and optional distances.\n *\n * @param params - Search parameters (either GeometrySearchParams or FuzzySearchParams)\n *\n * @returns Promise resolving to a collection of matching places\n *\n * @example\n * ```typescript\n * // Free-text search near a location\n * const results = await discoverPlaces({\n * query: 'pizza restaurant',\n * geoBias: { position: [4.9041, 52.3676] }, // Amsterdam\n * limit: 10\n * });\n *\n * // Search within a specific area\n * const areaResults = await discoverPlaces({\n * query: 'coffee shop',\n * geometries: [polygon], // Search within this polygon\n * limit: 20\n * });\n *\n * // Category search\n * const restaurants = await discoverPlaces({\n * poiCategories: ['ITALIAN_RESTAURANT'],\n * geoBias: { position: [4.9041, 52.3676], radiusMeters: 5000 } // Within 5km\n * });\n * ```\n *\n * @see [Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/search-service)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @group Places Search\n */\nexport const discoverPlaces = async (\n params: GeometrySearchParams | FuzzySearchParams | AlongRouteSearchParams,\n): Promise<DiscoverPlacesResponse> => {\n if ('route' in params) return alongRouteSearch(params);\n if ('geometries' in params) return geometrySearch(params);\n return fuzzySearch(params);\n};\n\n/**\n * Search for a single place by text query.\n *\n * Convenience function that calls {@link discoverPlaces} and returns the first result.\n * Throws an error if no results are found.\n *\n * @param queryOrParams - Search query string, or the full {@link FuzzySearchParams} (bar `limit`)\n * @returns Promise resolving to the first matching place\n * @throws {Error} If no results are found for the query\n *\n * @example\n * ```typescript\n * const place = await discoverOnePlace('Vondelpark Amsterdam');\n * console.log(place.properties.poi?.name);\n *\n * const nearby = await discoverOnePlace({ query: 'coffee', geoBias: { position: [4.9041, 52.3676] }, signal });\n * ```\n *\n * @remarks\n * * Useful to quickly find a single place, particularly a POI.\n * * If you want to find a single address, consider 'geocodeOne'.\n *\n * @group Places Search\n */\nexport const discoverOnePlace = async (\n queryOrParams: string | Omit<FuzzySearchParams, 'limit'>,\n): Promise<Place<SearchPlaceProps>> => fetchFirstPlace('discoverOnePlace', queryOrParams, discoverPlaces);\n","import { z } from 'zod';\nimport { pointGeoBiasSchema } from '../shared/schema/geoBiasSchema';\n\nconst autocompleteSearchRequestMandatory = z.object({\n query: z.string(),\n});\n\nconst autocompleteSearchRequestOptional = z.object({\n geoBias: pointGeoBiasSchema.optional(),\n limit: z.number().max(100).optional(),\n countries: z.array(z.string()).optional(),\n resultTypes: z.array(z.string()).optional(),\n});\n\n/**\n * @ignore\n */\nexport const autocompleteSearchRequestSchema = autocompleteSearchRequestMandatory.extend(\n autocompleteSearchRequestOptional.shape,\n);\n","import type { GetObject } from '../shared';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport {\n appendByJoiningParamValue,\n appendGeoBiasParams,\n appendOptionalParam,\n appendPlacesLanguageParam,\n buildPlacesRequestHeaders,\n} from '../shared/request/requestBuildingUtils';\nimport type { AutocompleteSearchParams } from './types';\n\nconst buildUrlBasePath = (mergedOptions: AutocompleteSearchParams): string =>\n resolvePlacesEndpointUrl(mergedOptions, `autocomplete/${encodeURIComponent(mergedOptions.query)}.json`);\n\n/**\n * Default function for building autocomplete request from {@link AutocompleteSearchParams}\n * @param params The autocomplete parameters, with global configuration already merged into them.\n */\nexport const buildAutocompleteSearchRequest = (params: AutocompleteSearchParams): GetObject => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n const urlParams = url.searchParams;\n /**\n * Auto-complete service defaults the language to en-GB if not specified explicitly as service param\n * Or global config\n */\n params.language = params.language ?? 'en-GB';\n appendPlacesLanguageParam(urlParams, params);\n appendOptionalParam(urlParams, 'limit', params.limit);\n appendGeoBiasParams(urlParams, params.geoBias);\n appendByJoiningParamValue(urlParams, 'countrySet', params.countries);\n appendByJoiningParamValue(urlParams, 'resultSet', params.resultTypes);\n\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import { poiIDsToCategories } from '@tomtom-org/maps-sdk/core';\nimport { latLonAPIToPosition } from '../shared/geometry';\nimport type {\n AutocompleteSearchCategorySegmentAPI,\n AutocompleteSearchResponse,\n AutocompleteSearchResponseAPI,\n AutocompleteSearchResultAPI,\n} from './types';\n\nconst isCategorySegment = (\n segment: AutocompleteSearchResultAPI['segments'][number],\n): segment is AutocompleteSearchCategorySegmentAPI => segment.type === 'category';\n\nconst parseResults = (results: AutocompleteSearchResultAPI[]): AutocompleteSearchResponse['results'] =>\n results.map((result) => ({\n segments: result.segments.map((segment) => {\n if (isCategorySegment(segment)) {\n const { id, ...rest } = segment;\n return { ...rest, category: poiIDsToCategories[Number(id)] };\n }\n return segment;\n }),\n }));\n\n/**\n * Default function to parse autocomplete response.\n * @param apiResponse The API response.\n */\nexport const parseAutocompleteSearchResponse = (\n apiResponse: AutocompleteSearchResponseAPI,\n): AutocompleteSearchResponse => {\n const { position, ...geoBias } = apiResponse.context.geoBias || {};\n return {\n context: {\n ...apiResponse.context,\n geoBias: {\n ...(position && { position: latLonAPIToPosition(position) }),\n radiusMeters: geoBias.radius,\n },\n },\n results: parseResults(apiResponse.results),\n };\n};\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { autocompleteSearchRequestSchema } from './autocompleteSearchRequestSchema';\nimport { buildAutocompleteSearchRequest } from './requestBuilder';\nimport { parseAutocompleteSearchResponse } from './responseParser';\nimport type { AutocompleteSearchParams, AutocompleteSearchResponse, AutocompleteSearchResponseAPI } from './types';\n\n/**\n * Autocomplete service template type.\n * @ignore\n */\nexport type AutocompleteSearchTemplate = ServiceTemplate<\n AutocompleteSearchParams,\n GetObject,\n AutocompleteSearchResponseAPI,\n AutocompleteSearchResponse\n>;\n\n/**\n * Autocomplete service template main implementation.\n * @ignore\n */\nexport const autocompleteSearchTemplate: AutocompleteSearchTemplate = {\n requestValidation: { schema: autocompleteSearchRequestSchema },\n buildRequest: buildAutocompleteSearchRequest,\n sendRequest: get,\n parseResponse: parseAutocompleteSearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport { autocompleteSearchTemplate } from './autocompleteSearchTemplate';\nimport type { AutocompleteSearchParams, AutocompleteSearchResponse } from './types';\n\n/**\n * Recognises the brands and categories a partial query names, as typed segments.\n *\n * @see [Autocomplete API Documentation](https://docs.tomtom.com/search-api/documentation/autocomplete-service/autocomplete)\n *\n * @ignore (exposed via 'getSearchSuggestions')\n */\nexport const autocompleteSearch = async (params: AutocompleteSearchParams): Promise<AutocompleteSearchResponse> =>\n callService(params, autocompleteSearchTemplate, 'Autocomplete');\n","import type { Place, SearchPlaceProps } from '@tomtom-org/maps-sdk/core';\nimport { partition, uniqBy } from 'lodash-es';\nimport type { AutocompleteSearchResult, AutocompleteSearchSegment } from '../autocomplete-search/types';\nimport type { PlaceSuggestion, RefinementSuggestion, SearchSuggestion } from './types/searchSuggestionTypes';\n\n/**\n * What the query names among brands and categories: nothing, a concept the places do not bear out,\n * or one to lead the list with.\n *\n * @ignore\n */\nexport type ConceptMatch =\n | { status: 'none' }\n | { status: 'withheld' }\n | { status: 'shown'; suggestion: RefinementSuggestion };\n\n// Accent- and case-insensitive words: \"Café De Gieter\" → [\"cafe\", \"de\", \"gieter\"], \"H&M\" → [\"h\", \"m\"].\nconst words = (text: string): string[] =>\n text\n .normalize('NFD')\n .replace(/\\p{Diacritic}/gu, '')\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean);\n\n// Whether the query reads as the start of `name` from one of its word boundaries, ignoring spacing and\n// punctuation: \"mcdon\" → \"McDonald's\", \"charging\" → \"Electric Vehicle Charging Station\", but not\n// \"amster\" → \"Mastermate\".\nconst startsAtWordOf = (name: string, query: string): boolean => {\n const target = words(query).join('');\n const nameWords = words(name);\n return target !== '' && nameWords.some((_, index) => nameWords.slice(index).join('').startsWith(target));\n};\n\n// A one-word query that is most of a word in `name` — \"burger\" in \"Burgers\" — rather than the first\n// letters of a long compound, as in \"Burgerluchthaven\".\nconst coversMostOfWordIn = (name: string, query: string): boolean => {\n const [queryWord, ...rest] = words(query);\n return (\n rest.length === 0 &&\n words(name).some((nameWord) => nameWord.startsWith(queryWord) && queryWord.length * 2 >= nameWord.length)\n );\n};\n\nconst namesOf = (segment: Exclude<AutocompleteSearchSegment, { type: 'plaintext' }>): string[] =>\n segment.type === 'category' && segment.matchedAlternativeName\n ? [segment.value, segment.matchedAlternativeName]\n : [segment.value];\n\n// Every typed word begins some word of the title: \"cafe\" matches \"Café De Gieter\", not \"Coffeeshop Tyson\".\nconst titleMatches = (title: string, query: string): boolean => {\n const titleWords = words(title);\n return words(query).every((queryWord) => titleWords.some((titleWord) => titleWord.startsWith(queryWord)));\n};\n\n/**\n * The brand or category segment the whole query names, if any.\n *\n * @remarks\n * An autocomplete result decomposes the whole query, so a single recognised segment covers everything\n * typed: \"hot\" → Hotel, but not \"baker st\", which leaves \"st\" as plaintext.\n *\n * The segment must also read as the query from a word start of its name or synonym, which rules out\n * \"Mastermate\" for \"amster\". The first segment the query covers most of a word of wins, else the first.\n *\n * @ignore\n */\nexport const findConceptSegment = (\n query: string,\n results: AutocompleteSearchResult[],\n): Exclude<AutocompleteSearchSegment, { type: 'plaintext' }> | undefined => {\n const candidates = results.flatMap(({ segments }) => {\n const [segment] = segments;\n return segments.length === 1 &&\n segment.type !== 'plaintext' &&\n namesOf(segment).some((name) => startsAtWordOf(name, query))\n ? [segment]\n : [];\n });\n return (\n candidates.find((segment) => namesOf(segment).some((name) => coversMostOfWordIn(name, query))) ?? candidates[0]\n );\n};\n\nconst BRAND_EVIDENCE_DEPTH = 5;\n\n/**\n * Whether the leading search results bear out a recognised brand.\n *\n * @remarks\n * A brand name that merely starts like the query — \"London Fire Brigade\" for \"londo\" — has no outlets\n * among the places the same query finds. A brand the user means does.\n *\n * @ignore\n */\nexport const searchCarriesBrand = (brand: string, places: Place<SearchPlaceProps>[]): boolean => {\n const target = words(brand).join('');\n return places\n .slice(0, BRAND_EVIDENCE_DEPTH)\n .some((place) => place.properties.poi?.brands?.some((name) => words(name).join('') === target));\n};\n\n/**\n * The default blend: at most one brand or category first, then places.\n *\n * @remarks\n * Modelled on Places Search v3's `suggest`, which never offers more than one concept and leads with it.\n *\n * Once the query names a brand or category, shown or withheld, that concept stands for \"places of this\n * kind\", so places named after the typed words move up: \"Café De Gieter\" ahead of a coffeeshop for \"cafe\".\n *\n * @ignore\n */\nexport const blendSuggestions = ({\n query,\n concept,\n places,\n maxResults,\n}: {\n query: string;\n concept: ConceptMatch;\n places: PlaceSuggestion[];\n maxResults: number;\n}): SearchSuggestion[] => {\n const distinctPlaces = uniqBy(places, (place) => place.id);\n const ordered =\n concept.status === 'none'\n ? distinctPlaces\n : partition(distinctPlaces, (place) => titleMatches(place.title, query)).flat();\n return [...(concept.status === 'shown' ? [concept.suggestion] : []), ...ordered].slice(0, maxResults);\n};\n","import {\n getPosition,\n type HasLngLat,\n type Place,\n type PlaceType,\n type SearchPlaceProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { Position } from 'geojson';\nimport { countBy, maxBy, minBy, omit, uniqBy } from 'lodash-es';\nimport { autocompleteSearch } from '../autocomplete-search/autocompleteSearch';\nimport type { AutocompleteSearchResult, AutocompleteSearchSegment } from '../autocomplete-search/types';\nimport type { FuzzySearchParams } from '../fuzzy-search/types';\nimport { discoverPlaces } from '../search/discoverPlaces';\nimport { SDKAbortError, SDKError, SDKServiceError, type SearchIndexType } from '../shared';\nimport { blendSuggestions, type ConceptMatch, findConceptSegment, searchCarriesBrand } from './blend';\nimport type {\n PlaceSuggestion,\n RefinementSuggestion,\n SearchSuggestionKind,\n SearchSuggestionsParams,\n SearchSuggestionsResponse,\n} from './types/searchSuggestionTypes';\n\nconst SERVICE_NAME = 'SearchSuggestions';\n\nconst DEFAULT_MAX_RESULTS = 5;\n// v3's `suggest` caps at 10; match it so the ceiling does not move when the backend does.\nconst MAX_RESULTS_CEILING = 10;\n\n// The two arms answer for disjoint halves of `SearchSuggestionKind`, so which kinds were asked for is\n// also which requests need to go out. Keyed on the refinement kinds so that a new one fails to\n// compile until it is listed; everything else is something the search arm produces.\nconst REFINEMENT_KINDS: Record<RefinementSuggestion['kind'], true> = { brand: true, category: true };\n\nconst isRefinementKind = (kind: SearchSuggestionKind): kind is RefinementSuggestion['kind'] => kind in REFINEMENT_KINDS;\n\nconst splitKinds = (kinds: SearchSuggestionKind[] | undefined) => {\n const placeKinds = kinds?.filter((kind) => !isRefinementKind(kind));\n const refinementKinds = kinds?.filter(isRefinementKind);\n return {\n placeKinds,\n refinementKinds,\n wantsPlaces: !kinds || Boolean(placeKinds?.length),\n wantsRefinements: !kinds || Boolean(refinementKinds?.length),\n };\n};\n\n// The two directions between suggestion kinds and V2's search indexes and place types. V2 has three\n// flavours of address that v3 folds into one; following v3 keeps the kinds stable across backends.\nconst INDEXES_BY_PLACE_KIND: Record<PlaceSuggestion['kind'], SearchIndexType[]> = {\n place: ['POI'],\n address: ['PAD', 'Addr', 'XStr'],\n street: ['Str'],\n area: ['Geo'],\n};\n\nconst KIND_BY_PLACE_TYPE: Record<PlaceType, PlaceSuggestion['kind']> = {\n POI: 'place',\n Street: 'street',\n Geography: 'area',\n 'Point Address': 'address',\n 'Address Range': 'address',\n 'Cross Street': 'address',\n};\n\nconst EARTH_RADIUS_METERS = 6371008.8;\n\nconst toRadians = (degrees: number) => (degrees * Math.PI) / 180;\n\n// Haversine: V2 measures `dist` only from a point it was sent, which a bounding-box search has none of.\nconst metersBetween = ([lng1, lat1]: Position, [lng2, lat2]: Position): number => {\n const halfChord =\n Math.sin(toRadians(lat2 - lat1) / 2) ** 2 +\n Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(toRadians(lng2 - lng1) / 2) ** 2;\n return 2 * EARTH_RADIUS_METERS * Math.asin(Math.sqrt(halfChord));\n};\n\nconst distanceFrom = (place: Place<SearchPlaceProps>, origin: Position | undefined): number | undefined =>\n place.properties.distance ?? (origin && metersBetween(origin, place.geometry.coordinates));\n\n// v3 leaves the caller's own country off the subtitles. V2 does not say where the caller is, so the\n// country of the place nearest the origin stands in for it, or without one the country most places are in.\nconst homeCountryCodeOf = (places: Place<SearchPlaceProps>[], origin: Position | undefined): string | undefined => {\n if (origin) return minBy(places, (place) => distanceFrom(place, origin))?.properties.address.countryCode;\n\n const counts = countBy(places, (place) => place.properties.address.countryCode);\n return maxBy(Object.keys(counts), (countryCode) => counts[countryCode]);\n};\n\n// The display fields v3 returns: a POI's name over its address lines, or an address's first line over\n// the rest. The search already returned the whole place, so resolving one is immediate; what only this\n// query decided stays on the suggestion, so the place matches what `getPlaceDetails` returns for its id.\nconst toPlaceSuggestion = (\n place: Place<SearchPlaceProps>,\n homeCountryCode: string | undefined,\n origin: Position | undefined,\n): PlaceSuggestion => {\n const { type, address, poi } = place.properties;\n const addressLines = address.freeformAddress.split(', ').filter(Boolean);\n const [title = '', ...subtitles] = poi?.name ? [poi.name, ...addressLines] : addressLines;\n const foreignCountry = address.countryCode !== homeCountryCode ? address.country : undefined;\n return {\n kind: KIND_BY_PLACE_TYPE[type],\n id: String(place.id),\n title,\n subtitles: foreignCountry ? [...subtitles, foreignCountry] : subtitles,\n distanceInMeters: distanceFrom(place, origin),\n resolve: async () => ({ ...place, properties: omit(place.properties, ['distance', 'score']) }),\n };\n};\n\nconst toPlaceSuggestions = (features: Place<SearchPlaceProps>[], origin: HasLngLat | undefined): PlaceSuggestion[] => {\n const originPosition = getPosition(origin) ?? undefined;\n const homeCountryCode = homeCountryCodeOf(features, originPosition);\n return features.map((place) => toPlaceSuggestion(place, homeCountryCode, originPosition));\n};\n\n// Either arm's failure is this call's, so it is reported under this call's name.\nconst throwAsSearchSuggestionsError = (error: unknown): never => {\n if (error instanceof SDKAbortError) throw new SDKAbortError(SERVICE_NAME, error.cause);\n if (error instanceof SDKServiceError) throw new SDKServiceError(error.message, SERVICE_NAME, error.status);\n if (error instanceof SDKError) throw new SDKError(error.message, SERVICE_NAME, error.issues);\n throw error;\n};\n\nconst toRefinement = (\n segment: Exclude<AutocompleteSearchSegment, { type: 'plaintext' }>,\n scope: FuzzySearchParams,\n): RefinementSuggestion => {\n const narrowing = segment.type === 'brand' ? { poiBrands: [segment.value] } : { poiCategories: [segment.category] };\n const refineParams: FuzzySearchParams = { ...scope, ...narrowing };\n const refine = (signal?: AbortSignal) => discoverPlaces({ ...refineParams, ...(signal && { signal }) });\n const shared = { title: segment.value, refineParams, refine };\n return segment.type === 'brand'\n ? { ...shared, kind: 'brand' }\n : { ...shared, kind: 'category', category: segment.category };\n};\n\n// Every recognised segment across the response, for a caller who asked for refinements only.\nconst toRefinementSuggestions = (\n results: AutocompleteSearchResult[],\n scope: FuzzySearchParams,\n): RefinementSuggestion[] =>\n uniqBy(\n results.flatMap((result) =>\n result.segments.flatMap((segment) => (segment.type === 'plaintext' ? [] : [toRefinement(segment, scope)])),\n ),\n (refinement) => `${refinement.kind}:${refinement.title}`,\n );\n\n// The concept to lead with, if any: the brand or category the query names, a brand only when the\n// places found for the same query bear it out.\nconst pickConcept = (\n query: string,\n results: AutocompleteSearchResult[],\n features: Place<SearchPlaceProps>[],\n scope: FuzzySearchParams,\n): ConceptMatch => {\n const segment = findConceptSegment(query, results);\n if (!segment) return { status: 'none' };\n\n if (segment.type === 'brand' && !searchCarriesBrand(segment.value, features)) return { status: 'withheld' };\n\n return { status: 'shown', suggestion: toRefinement(segment, scope) };\n};\n\n/**\n * One query surface over places, brands and categories.\n *\n * @remarks\n * Fans out to autocomplete and search in parallel and blends them into one ranked list, so \"pizz\"\n * gets the *Pizzeria* category and the pizzerias down the road together. A complete query works as\n * well as a partial one.\n *\n * - **Blend**: the SDK's, modelled on Places Search v3. At most one brand or category leads, and only\n * when the whole query names it; places follow.\n * - **Cost**: two requests per call; narrowing `filters.kinds` to one side of {@link SearchSuggestionKind}\n * skips the other. No debouncing or caching, which belong in the caller's input loop.\n * - **Geography**: `filters.boundingBox` restricts and `origin` measures distance. V2 takes one\n * geographic input per request, so inside a box `origin` does not rank the places.\n * - **Cancellation**: both requests share `signal`, so aborting drops the pair.\n *\n * @example\n * ```typescript\n * const { suggestions } = await getSearchSuggestions({ query: 'pizz' });\n *\n * for (const suggestion of suggestions) {\n * if (suggestion.kind === 'category' || suggestion.kind === 'brand') {\n * // A refinement: run the follow-up it describes.\n * const narrowed = await suggestion.refine();\n * } else {\n * const place = await suggestion.resolve();\n * map.flyTo({ center: place.geometry.coordinates });\n * }\n * }\n * ```\n *\n * @group Places Search\n */\nexport const getSearchSuggestions = async (parameters: SearchSuggestionsParams): Promise<SearchSuggestionsResponse> => {\n const { query, maxResults, filters, origin, view, signal, ...config } = parameters;\n const countries = filters?.countries;\n const max = Math.max(1, Math.min(maxResults ?? DEFAULT_MAX_RESULTS, MAX_RESULTS_CEILING));\n const { placeKinds, refinementKinds, wantsPlaces, wantsRefinements } = splitKinds(filters?.kinds);\n\n // V2 drops a box sent with a point, so a box restricts the places, in the list and in a follow-up\n // alike, and `origin` only measures.\n const originBias = origin && { position: origin };\n const placesBias = (filters?.boundingBox && { boundingBox: filters.boundingBox }) ?? originBias;\n\n // A follow-up inherits the caller's config and where to look, not the keystroke's signal, hooks,\n // limit, typeahead or API version. `config` holds only keys the caller set: an own `undefined`\n // blanks the global.\n const scope: FuzzySearchParams = {\n ...omit(config, ['onAPIRequest', 'onAPIResponse', 'apiVersion']),\n query: '',\n countries,\n geoBias: placesBias,\n view,\n };\n\n // The search arm always asks for the ceiling, so the blend has places to promote when a category\n // takes the first slot.\n const searchParams: FuzzySearchParams = {\n ...config,\n signal,\n query,\n limit: MAX_RESULTS_CEILING,\n countries,\n view,\n typeahead: true,\n geoBias: placesBias,\n indexes: placeKinds?.flatMap((kind) => INDEXES_BY_PLACE_KIND[kind]),\n poiCategories: filters?.poiTypes,\n geographyTypes: filters?.areaTypes,\n };\n\n const [searchResponse, autocompleteResponse] = await Promise.all([\n wantsPlaces ? discoverPlaces(searchParams) : undefined,\n wantsRefinements\n ? autocompleteSearch({\n ...config,\n signal,\n query,\n limit: max,\n countries,\n geoBias: originBias,\n resultTypes: refinementKinds ?? ['brand', 'category'],\n })\n : undefined,\n ]).catch(throwAsSearchSuggestionsError);\n\n const autocompleteResults = autocompleteResponse?.results ?? [];\n if (!wantsPlaces) return { suggestions: toRefinementSuggestions(autocompleteResults, scope).slice(0, max) };\n\n const features = (searchResponse?.features ?? []).filter(\n (place) => !placeKinds || placeKinds.includes(KIND_BY_PLACE_TYPE[place.properties.type]),\n );\n return {\n suggestions: blendSuggestions({\n query,\n concept: pickConcept(query, autocompleteResults, features, scope),\n places: toPlaceSuggestions(features, origin),\n maxResults: max,\n }),\n };\n};\n","import type { AreaAnalyticsDateInput } from './types/trafficAreaAnalyticsParams';\n\n/**\n * The latest date the API accepts, in days before today.\n */\nexport const LATEST_DATE_DAYS_AGO = 2;\n\n/**\n * The date an omitted `endDate` defaults to, in days before today: the API publishes a day 24 to 48 hours after it\n * ends, so the latest date it accepts has no data yet during part of each day.\n */\nexport const DEFAULT_END_DATE_DAYS_AGO = 3;\n\nexport const ONE_DAY_MS = 24 * 60 * 60 * 1000;\n\n// In UTC: the API works in `YYYY-MM-DD` dates, which are what `toISOString()` yields.\nexport const addDays = (date: Date, days: number): Date => {\n const result = new Date(date);\n result.setUTCDate(result.getUTCDate() + days);\n return result;\n};\n\nexport const daysAgo = (days: number): Date => addDays(new Date(), -days);\n\nexport const toDateString = (date: AreaAnalyticsDateInput): string =>\n date instanceof Date ? date.toISOString().slice(0, 10) : date;\n\n/**\n * Midnight UTC of the calendar day the request sends for `date`.\n */\nexport const startOfDayUTC = (date: AreaAnalyticsDateInput): number => Date.parse(toDateString(date));\n\nconst OFFSET = /(?:Z|[+-]\\d{2}:\\d{2})$/;\n\n/**\n * Reads a date-time from the response, such as `2024-08-06T07:00`, as UTC when it has no offset: the API's clock is\n * UTC, but `new Date` would read an offset-less date-time as local time.\n */\nexport const parseDateTime = (dateTime: string): Date => new Date(OFFSET.test(dateTime) ? dateTime : `${dateTime}Z`);\n","import type { AreaAnalyticsMetricKey } from '@tomtom-org/maps-sdk/core';\nimport type { MultiPolygon, Polygon } from 'geojson';\nimport type { CommonServiceParams } from '../../shared';\n\n// ---------------------------------------------------------------------------\n// Functional Road Classes\n// ---------------------------------------------------------------------------\n\n/**\n * Ordered list of Functional Road Class identifiers, from highest importance (motorways) to lowest.\n *\n * @remarks\n * The index of each class in this array corresponds to its FRC value in the API (0 for 'MOTORWAY', 1 for 'MAJOR_ROAD', ..., 8 for 'OTHER_ROAD').\n *\n * @see https://docs.tomtom.com/traffic-stats/documentation/product-information/faq#what-are-functional-road-classes-frc\n *\n * @group Traffic\n */\nexport const functionalRoadClasses = [\n 'MOTORWAY',\n 'MAJOR_ROAD',\n 'OTHER_MAJOR_ROAD',\n 'SECONDARY_ROAD',\n 'LOCAL_CONNECTING_ROAD',\n 'LOCAL_ROAD_HIGH_IMPORTANCE',\n 'LOCAL_ROAD',\n 'LOCAL_ROAD_MINOR_IMPORTANCE',\n 'OTHER_ROAD',\n] as const;\n\n/**\n * A Functional Road Class (FRC) identifier — classifies road segments by their importance\n * in the road network.\n *\n * | Value | Description |\n * |---|---|\n * | `'MOTORWAY'` | Motorways, freeways, and major roads |\n * | `'MAJOR_ROAD'` | Major roads, less important than motorways |\n * | `'OTHER_MAJOR_ROAD'` | Other major roads connecting neighbouring regions |\n * | `'SECONDARY_ROAD'` | Secondary roads linking parts of the same region |\n * | `'LOCAL_CONNECTING_ROAD'` | Local roads providing settlement access |\n * | `'LOCAL_ROAD_HIGH_IMPORTANCE'` | Local roads of high importance within a settlement |\n * | `'LOCAL_ROAD'` | Local roads within settlement sections |\n * | `'LOCAL_ROAD_MINOR_IMPORTANCE'` | Local roads of minor importance (dead-ends, alleys) |\n * | `'OTHER_ROAD'` | Other roads (paths, cycle routes, pedestrian infrastructure) |\n *\n * @group Traffic\n */\nexport type FunctionalRoadClass = (typeof functionalRoadClasses)[number];\n\n// ---------------------------------------------------------------------------\n// Date input\n// ---------------------------------------------------------------------------\n\n/**\n * A date value accepted by Traffic Area Analytics — either a `Date` object or\n * an ISO `'YYYY-MM-DD'` string.\n * `Date` objects are automatically converted to `YYYY-MM-DD` format before\n * being sent to the API.\n *\n * @group Traffic\n */\nexport type AreaAnalyticsDateInput = string | Date;\n\n// ---------------------------------------------------------------------------\n// Date selection — exactly one variant must be supplied\n// ---------------------------------------------------------------------------\n\n/**\n * Continuous date-range variant: analyse traffic between `startDate` and `endDate`.\n *\n * - `endDate` is optional; when omitted it defaults to **three days ago**, the latest date the\n * service always has data for.\n * - The range spans at least one and at most **31** UTC calendar days.\n * - Mutually exclusive with {@link TrafficAreaAnalyticsDays}.\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsDateRange = {\n /**\n * Start date of the analysis period.\n * Accepts a `Date` object or an ISO `'YYYY-MM-DD'` string.\n *\n * @example '2024-08-01'\n * @example new Date('2024-08-01')\n */\n startDate: AreaAnalyticsDateInput;\n\n /**\n * End date of the analysis period (inclusive).\n * Accepts a `Date` object or an ISO `'YYYY-MM-DD'` string.\n * When omitted, defaults to **three days ago**.\n *\n * @remarks Must be within 31 days from `startDate`, and no later than two days before today.\n *\n * @example '2024-08-07'\n * @example new Date('2024-08-07')\n */\n endDate?: AreaAnalyticsDateInput;\n\n days?: never;\n};\n\n/**\n * Specific-dates variant: analyse traffic on an explicit list of dates.\n *\n * Useful for non-consecutive dates such as \"all Mondays in August\".\n * Mutually exclusive with {@link TrafficAreaAnalyticsDateRange}.\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsDays = {\n /**\n * List of specific dates to include in the analysis.\n * Accepts `Date` objects or ISO `'YYYY-MM-DD'` strings.\n *\n * @example ['2024-08-05', '2024-08-12', '2024-08-19', '2024-08-26']\n */\n days: AreaAnalyticsDateInput[];\n\n startDate?: never;\n endDate?: never;\n};\n\n// ---------------------------------------------------------------------------\n// Main params type\n// ---------------------------------------------------------------------------\n\n/**\n * Parameters for the Traffic Area Analytics Lite service.\n *\n * @remarks\n * Supply **either** `startDate` (+ optional `endDate`) **or** `days` — these\n * two options are mutually exclusive.\n *\n * The lite endpoint runs analysis synchronously and returns results immediately,\n * with the constraint that the date range must not exceed 31 days and only a\n * single region may be submitted.\n *\n * @example\n * ```typescript\n * // Continuous range: endDate defaults to three days ago when omitted\n * const result = await trafficAreaAnalytics({\n * startDate: '2024-08-01',\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],\n * hours: [7, 8, 9, 17, 18],\n * metrics: ['speed', 'congestionLevel'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.896128, 52.382402], [4.875701, 52.368459], [4.923611, 52.36341], [4.896128, 52.382402]]]\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Specific dates\n * const result = await trafficAreaAnalytics({\n * days: ['2024-08-05', '2024-08-12', '2024-08-19', '2024-08-26'],\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD'],\n * hours: [8, 9, 17, 18],\n * metrics: ['speed'],\n * geometry: polygon\n * });\n * ```\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsParams = CommonServiceParams &\n TrafficAreaAnalyticsBaseParams &\n (TrafficAreaAnalyticsDateRange | TrafficAreaAnalyticsDays);\n\n/**\n * @ignore\n */\ntype TrafficAreaAnalyticsBaseParams = {\n /**\n * Optional name for the analysis report.\n */\n name?: string;\n\n /**\n * Traffic metrics to include in the analysis, or `'all'` to include every metric.\n *\n * @remarks\n * At least one metric must be provided, or pass `'all'` as a shorthand for all five metrics.\n *\n * @example ['speed', 'congestionLevel', 'freeFlowSpeed']\n * @example 'all'\n */\n metrics: AreaAnalyticsMetricKey[] | 'all';\n\n /**\n * Functional road classes to include in the analysis, or `'all'` to include every class.\n *\n * @remarks\n * At least one value must be provided, or pass `'all'` as a shorthand for all nine classes.\n *\n * @example ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD']\n * @example 'all'\n */\n functionalRoadClasses: FunctionalRoadClass[] | 'all';\n\n /**\n * Hours of the day in UTC (0–23) to include in the analysis, or `'all'` to include every hour.\n *\n * @remarks\n * Provide at least one hour, or pass `'all'` as a shorthand for `[0, 1, 2, ..., 23]`.\n *\n * @example [7, 8, 9, 17, 18]\n * @example 'all'\n */\n hours: number[] | 'all';\n\n /**\n * The GeoJSON Polygon or MultiPolygon geometry defining the analysis region.\n *\n * @remarks\n * The lite endpoint accepts exactly one region. For multi-region analysis,\n * use the standard (async) report creation endpoint.\n */\n geometry: Polygon | MultiPolygon;\n};\n","import { type AreaAnalyticsMetricKey, areaAnalyticsMetricKeys } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput } from '../shared';\nimport { appendCommonParams } from '../shared/request/requestBuildingUtils';\nimport { DEFAULT_END_DATE_DAYS_AGO, daysAgo, toDateString } from './dates';\nimport type { AreaAnalyticsDataTypeAPI, AreaAnalyticsRequestBody, MetricsAPI } from './types/apiTypes';\nimport type { FunctionalRoadClass, TrafficAreaAnalyticsParams } from './types/trafficAreaAnalyticsParams';\nimport { functionalRoadClasses } from './types/trafficAreaAnalyticsParams';\n\nconst AREA_ANALYTICS_URL_PATH = '/areaanalytics/reports/lite';\n\n/**\n * Each metric's names in the API: its data type in the request and the response `dataTypes`, and its abbreviated\n * field in the response metric values.\n */\nexport const API_NAMES_BY_METRIC_KEY: Record<\n AreaAnalyticsMetricKey,\n { dataType: AreaAnalyticsDataTypeAPI; field: keyof MetricsAPI }\n> = {\n speed: { dataType: 'SPEED', field: 'v' },\n freeFlowSpeed: { dataType: 'FREE_FLOW_SPEED', field: 'fv' },\n congestionLevel: { dataType: 'CONGESTION_LEVEL', field: 'c' },\n travelTime: { dataType: 'TRAVEL_TIME', field: 't' },\n networkLength: { dataType: 'NETWORK_LENGTH', field: 'l' },\n};\n\nconst buildUrlBasePath = (params: TrafficAreaAnalyticsParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${AREA_ANALYTICS_URL_PATH}`;\n\nexport const ALL_HOURS = Array.from({ length: 24 }, (_, i) => i);\n\nconst frcToNumber = (frc: FunctionalRoadClass): number => functionalRoadClasses.indexOf(frc);\n\n/**\n * Default method for building a Traffic Area Analytics request from {@link TrafficAreaAnalyticsParams}.\n * @param params The traffic area analytics parameters, with global configuration already merged.\n */\nexport const buildTrafficAreaAnalyticsRequest = (\n params: TrafficAreaAnalyticsParams,\n): FetchInput<AreaAnalyticsRequestBody> => {\n const url = new URL(buildUrlBasePath(params));\n appendCommonParams(url.searchParams, params);\n\n const datePart = Array.isArray(params.days)\n ? { days: params.days.map(toDateString) }\n : {\n startDate: toDateString(params.startDate ?? daysAgo(DEFAULT_END_DATE_DAYS_AGO + 1)),\n endDate: toDateString(params.endDate ?? daysAgo(DEFAULT_END_DATE_DAYS_AGO)),\n };\n\n const body: AreaAnalyticsRequestBody = {\n name: params.name ?? `Maps SDK JS Area Analytics Report ${new Date().toISOString()}`,\n ...datePart,\n dataTypes: (params.metrics === 'all' ? areaAnalyticsMetricKeys : params.metrics).map(\n (metricKey) => API_NAMES_BY_METRIC_KEY[metricKey].dataType,\n ),\n frcs:\n params.functionalRoadClasses === 'all'\n ? functionalRoadClasses.map((_, i) => i)\n : params.functionalRoadClasses.map(frcToNumber),\n hours: params.hours === 'all' ? ALL_HOURS : params.hours,\n features: [{ type: 'Feature', geometry: params.geometry }],\n };\n\n return { method: 'POST', url, data: body };\n};\n","import {\n AreaAnalyticsAnomaly,\n AreaAnalyticsAverageEntry,\n AreaAnalyticsCollectionProperties,\n AreaAnalyticsFeature,\n AreaAnalyticsFeatureProperties,\n AreaAnalyticsMetricKey,\n AreaAnalyticsMetricRange,\n AreaAnalyticsMetrics,\n AreaAnalyticsTileEntry,\n AreaAnalyticsTimedData,\n areaAnalyticsMetricKeys,\n BBox,\n bboxFromGeoJSON,\n TrafficAreaAnalytics,\n} from '@tomtom-org/maps-sdk/core';\nimport { addDays, ONE_DAY_MS, parseDateTime, startOfDayUTC } from './dates';\nimport { API_NAMES_BY_METRIC_KEY } from './requestBuilder';\nimport type {\n AnomaliesAPI,\n AnomalyAPI,\n AreaAnalyticsCollectionPropertiesAPI,\n AreaAnalyticsFeatureAPI,\n AreaAnalyticsResponseAPI,\n AverageEntryAPI,\n DayOfWeekAPI,\n FeaturePropertiesAPI,\n MetricFieldsAPI,\n MetricsAPI,\n TiledEntryAPI,\n TimedEntryAPI,\n} from './types/apiTypes';\n\ntype ByMetricKey<Value> = Partial<Record<AreaAnalyticsMetricKey, Value>>;\n\n/**\n * Renames the abbreviated metric fields the API sent, parsing each value, and leaves out the ones it did not send.\n */\nconst parseMetricFields = <APIValue, Value>(\n api: MetricFieldsAPI<APIValue>,\n parseValue: (apiValue: APIValue) => Value,\n): ByMetricKey<Value> => {\n const result: ByMetricKey<Value> = {};\n for (const metricKey of areaAnalyticsMetricKeys) {\n const apiValue = api[API_NAMES_BY_METRIC_KEY[metricKey].field];\n if (apiValue !== undefined) result[metricKey] = parseValue(apiValue);\n }\n return result;\n};\n\nconst parseMetrics = (api: MetricsAPI): AreaAnalyticsMetrics => parseMetricFields(api, (value) => value);\n\n// The response may name data types this SDK version doesn't know.\nconst parseMetricKey = (dataType: string): AreaAnalyticsMetricKey | undefined =>\n areaAnalyticsMetricKeys.find((metricKey) => API_NAMES_BY_METRIC_KEY[metricKey].dataType === dataType);\n\ntype PeriodStart = { year: number; month: number; date: Date; hour: number };\n\nconst parsePeriodStart = (time: TimedEntryAPI['time']): PeriodStart => {\n const start = parseDateTime(time);\n return {\n year: start.getUTCFullYear(),\n month: start.getUTCMonth() + 1,\n date: new Date(startOfDayUTC(start)),\n hour: start.getUTCHours(),\n };\n};\n\ntype CalendarGranularity = Exclude<keyof AreaAnalyticsTimedData, 'average'>;\n\ntype TimeIdentifiers<Granularity extends CalendarGranularity> = Omit<\n NonNullable<AreaAnalyticsTimedData[Granularity]>[number],\n AreaAnalyticsMetricKey\n>;\n\n// An ISO 8601 week belongs to the year of its Thursday, and week 1 is the one holding that year's first Thursday.\nconst parseISOWeek = (date: Date): TimeIdentifiers<'weekly'> => {\n const thursday = addDays(date, 4 - (date.getUTCDay() || 7));\n const year = thursday.getUTCFullYear();\n const dayOfYear = (thursday.getTime() - Date.UTC(year, 0, 1)) / ONE_DAY_MS;\n return { year, week: Math.floor(dayOfYear / 7) + 1 };\n};\n\nconst IDENTIFIERS_BY_GRANULARITY: {\n [Granularity in CalendarGranularity]: (start: PeriodStart) => TimeIdentifiers<Granularity>;\n} = {\n yearly: ({ year }) => ({ year }),\n monthly: ({ year, month }) => ({ year, month }),\n weekly: ({ date }) => parseISOWeek(date),\n daily: ({ date }) => ({ date }),\n hourly: ({ date, hour }) => ({ date, hour }),\n};\n\nconst parseCalendarEntries = <Granularity extends CalendarGranularity>(\n entries: TimedEntryAPI[],\n granularity: Granularity,\n): (TimeIdentifiers<Granularity> & AreaAnalyticsMetrics)[] =>\n entries.map((entry) => ({\n ...IDENTIFIERS_BY_GRANULARITY[granularity](parsePeriodStart(entry.time)),\n ...parseMetrics(entry),\n }));\n\ntype DayOfWeek = AreaAnalyticsAverageEntry['day'];\n\n// Keyed by `string`: the response may name a day this SDK version doesn't know, and its entry is dropped.\nconst DAY_BY_API_DAY_OF_WEEK: Partial<Record<string, DayOfWeek>> = {\n MONDAY: 1,\n TUESDAY: 2,\n WEDNESDAY: 3,\n THURSDAY: 4,\n FRIDAY: 5,\n SATURDAY: 6,\n SUNDAY: 7,\n} satisfies Record<DayOfWeekAPI, DayOfWeek>;\n\nconst parseAverageEntry = (entry: AverageEntryAPI): AreaAnalyticsAverageEntry | undefined => {\n const [apiDay, hour] = entry.time.split('-');\n // `hasOwn` keeps out the `Object.prototype` keys, such as `toString`.\n const day = Object.hasOwn(DAY_BY_API_DAY_OF_WEEK, apiDay) ? DAY_BY_API_DAY_OF_WEEK[apiDay] : undefined;\n return day === undefined ? undefined : { day, hour: Number(hour), ...parseMetrics(entry) };\n};\n\nconst hourOfWeek = ({ day, hour }: AreaAnalyticsAverageEntry): number => (day - 1) * 24 + hour;\n\nconst parseAverageEntries = (entries: AverageEntryAPI[]): AreaAnalyticsAverageEntry[] =>\n entries\n .map(parseAverageEntry)\n .filter((entry) => entry !== undefined)\n .sort((first, second) => hourOfWeek(first) - hourOfWeek(second));\n\nconst parseTileEntry = (tile: TiledEntryAPI): AreaAnalyticsTileEntry => ({\n tileCentre: [tile.lon, tile.lat],\n ...parseMetrics(tile),\n});\n\nconst parseAnomaly = (api: AnomalyAPI): AreaAnalyticsAnomaly => ({\n startDate: parseDateTime(api.startDate),\n endDate: parseDateTime(api.endDate),\n labels: api.labels.map(({ name }) => name),\n});\n\nconst parseTimedData = (api: FeaturePropertiesAPI['timedData']): AreaAnalyticsTimedData => ({\n ...(api.yearly && { yearly: parseCalendarEntries(api.yearly, 'yearly') }),\n ...(api.monthly && { monthly: parseCalendarEntries(api.monthly, 'monthly') }),\n ...(api.weekly && { weekly: parseCalendarEntries(api.weekly, 'weekly') }),\n ...(api.daily && { daily: parseCalendarEntries(api.daily, 'daily') }),\n ...(api.hourly && { hourly: parseCalendarEntries(api.hourly, 'hourly') }),\n ...(api.average && { average: parseAverageEntries(api.average) }),\n});\n\nconst parseAnomalies = (api: AnomaliesAPI): NonNullable<AreaAnalyticsFeatureProperties['anomalies']> =>\n parseMetricFields(api, (anomalies) => anomalies.map(parseAnomaly));\n\nconst parseFeatureProperties = (api: FeaturePropertiesAPI): AreaAnalyticsFeatureProperties => ({\n name: api.name,\n ...(api.timezone !== undefined && { timezone: api.timezone }),\n level: api.level,\n baseData: parseMetrics(api.baseData),\n timedData: parseTimedData(api.timedData),\n ...(api.tiledData && {\n tiledData: { tiles: api.tiledData.tiles.map(parseTileEntry) },\n }),\n ...(api.anomalies && {\n anomalies: parseAnomalies(api.anomalies),\n }),\n});\n\nconst parseFeature = (apiFeature: AreaAnalyticsFeatureAPI): AreaAnalyticsFeature => ({\n type: apiFeature.type,\n id: apiFeature.id,\n geometry: apiFeature.geometry,\n properties: parseFeatureProperties(apiFeature.properties),\n bbox: bboxFromGeoJSON(apiFeature.geometry) as BBox,\n});\n\nconst computeTileRanges = (features: AreaAnalyticsFeature[]): ByMetricKey<AreaAnalyticsMetricRange> => {\n const tiles = features.flatMap((f) => f.properties.tiledData?.tiles ?? []);\n const ranges: ByMetricKey<AreaAnalyticsMetricRange> = {};\n for (const key of areaAnalyticsMetricKeys) {\n const values = tiles.map((t) => t[key]).filter((v): v is number => v != null);\n if (values.length > 0) {\n ranges[key] = { min: Math.min(...values), max: Math.max(...values) };\n }\n }\n return ranges;\n};\n\n// The earliest and latest day, not the first and last: `days` comes in no guaranteed order.\nconst parseAnalysisPeriod = (\n days: AreaAnalyticsCollectionPropertiesAPI['days'],\n): Pick<AreaAnalyticsCollectionProperties, 'startDate' | 'endDate'> => {\n const times = days.map(startOfDayUTC);\n return { startDate: new Date(Math.min(...times)), endDate: new Date(Math.max(...times)) };\n};\n\n/**\n * Parses a raw Traffic Area Analytics API response into the SDK representation.\n *\n * In addition to renaming abbreviated metric fields and converting date strings\n * to `Date` objects, this function computes data-driven min/max ranges from\n * all tile entries across every feature and attaches them to\n * `result.properties.ranges`.\n *\n * @param apiResponse - The raw Area Analytics API response.\n */\nexport const parseTrafficAreaAnalyticsResponse = (apiResponse: AreaAnalyticsResponseAPI): TrafficAreaAnalytics => {\n const features = apiResponse.features.map(parseFeature);\n\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseAnalysisPeriod(apiResponse.properties.days),\n metrics: apiResponse.properties.dataTypes\n .map(parseMetricKey)\n .filter((metricKey) => metricKey !== undefined),\n heatmap: apiResponse.properties.heatmap,\n frcs: apiResponse.properties.frcs,\n ranges: computeTileRanges(features),\n },\n features,\n };\n};\n","import { areaAnalyticsMetricKeys } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { DEFAULT_END_DATE_DAYS_AGO, daysAgo, LATEST_DATE_DAYS_AGO, ONE_DAY_MS, startOfDayUTC } from './dates';\nimport { type AreaAnalyticsDateInput, functionalRoadClasses } from './types/trafficAreaAnalyticsParams';\n\nconst dateInputSchema = z.union([\n z.date(),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"Date must be in 'YYYY-MM-DD' format\"),\n]);\n\nconst geometrySchema = z.union([\n z.object({\n type: z.literal('Polygon'),\n coordinates: z.array(z.array(z.array(z.number()))).min(1),\n }),\n z.object({\n type: z.literal('MultiPolygon'),\n coordinates: z.array(z.array(z.array(z.array(z.number())))).min(1),\n }),\n]);\n\nconst isAccepted = (date: AreaAnalyticsDateInput): boolean =>\n startOfDayUTC(date) <= startOfDayUTC(daysAgo(LATEST_DATE_DAYS_AGO));\n\n/**\n * @ignore\n */\nexport const trafficAreaAnalyticsRequestSchema = commonServiceRequestSchema\n .extend({\n name: z.string().optional(),\n startDate: dateInputSchema.optional(),\n endDate: dateInputSchema.optional(),\n days: z.array(dateInputSchema).min(1).optional(),\n metrics: z.union([z.array(z.enum(areaAnalyticsMetricKeys)).min(1), z.literal('all')]),\n functionalRoadClasses: z.union([z.array(z.enum(functionalRoadClasses)).min(1), z.literal('all')]),\n hours: z.union([z.array(z.number().int().min(0).max(23)).min(1), z.literal('all')]),\n geometry: geometrySchema,\n })\n .refine(\n (data) => {\n const hasDays = data.days !== undefined;\n const hasStart = data.startDate !== undefined;\n\n // Exactly one mode: either days OR startDate (+optional endDate)\n if (hasDays && hasStart) return false;\n if (!hasDays && !hasStart) return false;\n\n // Validate range for continuous range: startDate must be at least 1 day before endDate, within 31 days\n if (data.startDate !== undefined) {\n const endDate = data.endDate ?? daysAgo(DEFAULT_END_DATE_DAYS_AGO);\n const diffDays = (startOfDayUTC(endDate) - startOfDayUTC(data.startDate)) / ONE_DAY_MS;\n return diffDays >= 1 && diffDays <= 31;\n }\n\n return true;\n },\n {\n message:\n 'Provide either startDate (with optional endDate, at least 1 day apart and within 31 days) or days — but not both',\n },\n )\n .refine(\n (data) => {\n if (data.days !== undefined) {\n return data.days.every(isAccepted);\n }\n\n return data.endDate === undefined || isAccepted(data.endDate);\n },\n {\n message: `Dates must be at least ${LATEST_DATE_DAYS_AGO} days before today`,\n },\n );\n","import type { TrafficAreaAnalytics } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { buildTrafficAreaAnalyticsRequest } from './requestBuilder';\nimport { parseTrafficAreaAnalyticsResponse } from './responseParser';\nimport { trafficAreaAnalyticsRequestSchema } from './trafficAreaAnalyticsRequestSchema';\nimport type { AreaAnalyticsRequestBody, AreaAnalyticsResponseAPI } from './types/apiTypes';\nimport type { TrafficAreaAnalyticsParams } from './types/trafficAreaAnalyticsParams';\n\n/**\n * Traffic Area Analytics service template type.\n * @ignore\n */\nexport type TrafficAreaAnalyticsTemplate = ServiceTemplate<\n TrafficAreaAnalyticsParams,\n FetchInput<AreaAnalyticsRequestBody>,\n AreaAnalyticsResponseAPI,\n TrafficAreaAnalytics\n>;\n\n/**\n * Traffic Area Analytics service template main implementation.\n * @ignore\n */\nexport const trafficAreaAnalyticsTemplate: TrafficAreaAnalyticsTemplate = {\n requestValidation: { schema: trafficAreaAnalyticsRequestSchema },\n buildRequest: buildTrafficAreaAnalyticsRequest,\n sendRequest: fetchWith,\n parseResponse: parseTrafficAreaAnalyticsResponse,\n};\n","import type { TrafficAreaAnalytics } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { trafficAreaAnalyticsTemplate } from './trafficAreaAnalyticsTemplate';\nimport type { TrafficAreaAnalyticsParams } from './types/trafficAreaAnalyticsParams';\n\n/**\n * Run a synchronous traffic area analytics report for a polygon region.\n *\n * Submits a single Polygon region with a date range and requested metrics, and\n * receives the full analysis result immediately (no polling required).\n *\n * @remarks\n * **Access**: needs a [TomTom Move Portal](https://move.tomtom.com) API key, passed as `apiKey` on the call, and is\n * unavailable on a Freemium or Pay As You Grow (PAYG) basis: [request access from TomTom Sales](https://www.tomtom.com/contact-sales/?source_app=docs-portal&source_product=traffic-area-analytics).\n *\n * **Lite report constraints:**\n * - Only a single region (Polygon Feature) may be submitted per request.\n * - The date range (`startDate` → `endDate`) must not exceed 31 days.\n *\n * **Key data returned per region:**\n * - `baseData`: overall aggregated metrics for the full analysis period\n * - `timedData`: time-series breakdown at yearly / monthly / weekly / daily / hourly granularity, and an average\n * week, all in UTC\n * - `tiledData`: per-tile heatmap metrics (when available)\n * - `anomalies`: detected traffic anomalies keyed by camelCase metric key (e.g. `anomalies.speed`), empty since\n * the service detects them only over 35 days or more\n *\n * **Available metrics (`metrics`):**\n * - `speed` — average speed on the road network (km/h)\n * - `freeFlowSpeed` — average speed under uncongested conditions (km/h)\n * - `congestionLevel` — percentage increase in travel time above free-flow\n * - `travelTime` — average travel time per 10 km (minutes)\n * - `networkLength` — total length of road segments with data (meters)\n *\n * @param params - Traffic Area Analytics parameters\n *\n * @returns Promise resolving to a {@link TrafficAreaAnalytics} FeatureCollection\n *\n * @example\n * ```typescript\n * // Basic speed and congestion analysis for Amsterdam\n * const result = await trafficAreaAnalytics({\n * startDate: new Date('2024-08-06'),\n * endDate: new Date('2024-08-06'),\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],\n * hours: [7, 8, 9, 17, 18],\n * metrics: ['speed', 'congestionLevel'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [\n * [[4.896128, 52.382402], [4.875701, 52.368459], [4.923611, 52.36341], [4.896128, 52.382402]]\n * ]\n * }\n * });\n *\n * const region = result.features[0];\n * console.log(region.properties.baseData.speed); // avg speed (km/h)\n * console.log(region.properties.baseData.congestionLevel); // congestion %\n * console.log(region.properties.timedData.hourly); // hourly breakdown\n * ```\n *\n * @example\n * ```typescript\n * // Full metrics over a multi-day period\n * const result = await trafficAreaAnalytics({\n * startDate: new Date('2024-08-01'),\n * endDate: new Date('2024-08-07'),\n * functionalRoadClasses: 'all',\n * hours: 'all',\n * metrics: ['speed', 'freeFlowSpeed', 'congestionLevel', 'travelTime', 'networkLength'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.89, 52.37], [4.91, 52.37], [4.91, 52.39], [4.89, 52.39], [4.89, 52.37]]]\n * }\n * });\n *\n * result.features[0].properties.timedData.daily?.forEach(day => {\n * console.log(day.date, day.congestionLevel);\n * });\n * ```\n *\n * @see [Area Analytics API Documentation](https://developer.tomtom.com/area-analytics/documentation/api/analysis-lite)\n * @see [Traffic Area Analytics Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/traffic/traffic-area-analytics)\n *\n * @group Traffic\n */\nexport const trafficAreaAnalytics = async (params: TrafficAreaAnalyticsParams): Promise<TrafficAreaAnalytics> =>\n callService(params, trafficAreaAnalyticsTemplate, 'TrafficAreaAnalytics');\n","import { bboxFromGeoJSON, type TomTomAPIHeaders } from '@tomtom-org/maps-sdk/core';\nimport { bbox } from '@turf/turf';\nimport type { FetchInput } from '../shared';\nimport { appendByJoiningParamValue, buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport { toIconCategory } from '../shared/trafficIncidents';\nimport type { IncidentEventAPI, IncidentPropertiesAPI } from './types/apiTypes';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\nconst TRAFFIC_URL_PATH = '/maps/orbis/traffic/incidents/details';\n\ntype IncidentAttribute = Exclude<keyof IncidentPropertiesAPI, 'events'> | `events.${keyof IncidentEventAPI}`;\n\n// v2 returns only the fields `Attributes` names and rejects a bare `*` or an unknown name. Keyed on the\n// wire type, so a field added to it without being requested here fails to compile.\nconst INCIDENT_PROPERTIES: Record<IncidentAttribute, true> = {\n id: true,\n iconCategory: true,\n magnitudeOfDelay: true,\n 'events.description': true,\n 'events.code': true,\n 'events.iconCategory': true,\n startTime: true,\n endTime: true,\n from: true,\n to: true,\n lengthInMeters: true,\n delayInSeconds: true,\n roadNumbers: true,\n timeValidity: true,\n probabilityOfOccurrence: true,\n numberOfReports: true,\n lastReportTime: true,\n};\nconst DEFAULT_ATTRIBUTES = [\n 'incidents.type',\n 'incidents.geometry.type',\n 'incidents.geometry.coordinates',\n ...Object.keys(INCIDENT_PROPERTIES).map((property) => `incidents.properties.${property}`),\n].join(',');\n\nconst MAX_GET_IDS = 5;\n\ntype TrafficIncidentDetailsPostBody = { ids: string[] };\n\nconst buildUrlBasePath = (params: TrafficIncidentDetailsParams): string =>\n params.customServiceBaseURL ?? `${params.commonBaseURL}${TRAFFIC_URL_PATH}`;\n\nconst appendOptionalParams = (urlParams: URLSearchParams, params: TrafficIncidentDetailsParams): void => {\n appendByJoiningParamValue(urlParams, 'iconCategories', params.categoryFilter?.map(toIconCategory));\n appendByJoiningParamValue(urlParams, 'timeValidity', params.timeValidityFilter);\n};\n\nconst buildHeaders = (params: TrafficIncidentDetailsParams): TomTomAPIHeaders => ({\n ...buildCommonServiceRequestHeaders(params),\n Attributes: DEFAULT_ATTRIBUTES,\n});\n\n/**\n * Default method for building a traffic incident details request from {@link TrafficIncidentDetailsParams}.\n * @param params The traffic incident details parameters, with global configuration already merged into them.\n */\nexport const buildTrafficIncidentDetailsRequest = (\n params: TrafficIncidentDetailsParams,\n): FetchInput<TrafficIncidentDetailsPostBody> => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n appendOptionalParams(urlParams, params);\n const headers = buildHeaders(params);\n\n if (params.ids) {\n const { ids } = params;\n if (ids.length > MAX_GET_IDS) {\n return { method: 'POST', url, headers, data: { ids } };\n }\n\n urlParams.append('ids', ids.join(','));\n return { method: 'GET', url, headers };\n }\n\n // The API only takes a bounding box: a polygon query sends its box and the parser trims the rest. That box\n // reads every coordinate, where `bboxFromGeoJSON` would trust an embedded `bbox` and sample long rings.\n const resolvedBBox = params.polygon ? bbox(params.polygon, { recompute: true }) : bboxFromGeoJSON(params.bbox);\n if (resolvedBBox) {\n urlParams.append('bbox', resolvedBBox.join(','));\n }\n return { method: 'GET', url, headers };\n};\n","import type { TrafficIncident, TrafficIncidentDetails, TrafficIncidentEvent } from '@tomtom-org/maps-sdk/core';\nimport { bbox, booleanIntersects } from '@turf/turf';\nimport type { BBox, MultiPolygon, Polygon } from 'geojson';\nimport { toDelayMagnitude, toTrafficIncidentCategory } from '../shared/trafficIncidents';\nimport type { IncidentAPI, IncidentDetailsResponseAPI, IncidentEventAPI } from './types/apiTypes';\nimport type {\n TrafficIncidentDetailsByPolygonParams,\n TrafficIncidentDetailsParams,\n} from './types/trafficIncidentDetailsParams';\n\ntype QueryPolygon = TrafficIncidentDetailsByPolygonParams['polygon'];\n\nconst parseEvent = (apiEvent: IncidentEventAPI): TrafficIncidentEvent => ({\n description: apiEvent.description,\n code: apiEvent.code,\n category: toTrafficIncidentCategory(apiEvent.iconCategory),\n});\n\nconst parseIncident = (apiIncident: IncidentAPI): TrafficIncident => {\n const properties = apiIncident.properties;\n return {\n type: 'Feature',\n // Top-level id is what the SDK's event-state machinery uses to track per-feature\n // click/hover state (see `renderedRefId` in map's featureId.ts). promoteId: 'id' on the\n // source keeps rendered features pointing at the same identifier.\n id: properties.id,\n geometry: apiIncident.geometry,\n properties: {\n id: properties.id,\n category: toTrafficIncidentCategory(properties.iconCategory),\n magnitudeOfDelay: toDelayMagnitude(properties.magnitudeOfDelay),\n events: (properties.events ?? []).map(parseEvent),\n ...(properties.startTime && { startTime: new Date(properties.startTime) }),\n ...(properties.endTime && { endTime: new Date(properties.endTime) }),\n ...(properties.from && { from: properties.from }),\n ...(properties.to && { to: properties.to }),\n // `!= null` drops the wire `null` too, which MapLibre's numeric paint/filter expressions reject.\n ...(properties.lengthInMeters != null && { lengthInMeters: properties.lengthInMeters }),\n ...(properties.delayInSeconds != null && { delayInSeconds: properties.delayInSeconds }),\n ...(properties.roadNumbers && { roadNumbers: properties.roadNumbers }),\n timeValidity: properties.timeValidity,\n ...(properties.probabilityOfOccurrence && { probabilityOfOccurrence: properties.probabilityOfOccurrence }),\n ...(properties.numberOfReports != null && { numberOfReports: properties.numberOfReports }),\n ...(properties.lastReportTime && { lastReportTime: new Date(properties.lastReportTime) }),\n },\n };\n};\n\nconst toGeometries = (polygon: QueryPolygon): (Polygon | MultiPolygon)[] => {\n if (polygon.type === 'FeatureCollection') {\n return polygon.features.map((feature) => feature.geometry);\n }\n\n return [polygon.type === 'Feature' ? polygon.geometry : polygon];\n};\n\n// Turf's predicates trust an embedded `bbox`, so a stale one would drop incidents inside the polygon.\nconst withoutBBox = (geometry: Polygon | MultiPolygon): Polygon | MultiPolygon =>\n geometry.type === 'Polygon'\n ? { type: 'Polygon', coordinates: geometry.coordinates }\n : { type: 'MultiPolygon', coordinates: geometry.coordinates };\n\nconst bboxesIntersect = (first: BBox, second: BBox): boolean =>\n first[0] <= second[2] && second[0] <= first[2] && first[1] <= second[3] && second[1] <= first[3];\n\n// Intersection rather than containment, so a LineString incident crossing the boundary is kept. The bbox test\n// spares the per-vertex `booleanIntersects` for the parts an incident is nowhere near.\nconst incidentsIntersecting = (incidents: TrafficIncident[], polygon: QueryPolygon): TrafficIncident[] => {\n const parts = toGeometries(polygon)\n .map(withoutBBox)\n .map((geometry) => ({ geometry, bbox: bbox(geometry) }));\n return incidents.filter((incident) => {\n const incidentBBox = bbox(incident.geometry);\n return parts.some(\n (part) => bboxesIntersect(incidentBBox, part.bbox) && booleanIntersects(incident.geometry, part.geometry),\n );\n });\n};\n\n/**\n * Default method for parsing a traffic incident details API response.\n * @param apiResponse The raw Traffic Incident Details API response.\n * @param params The request parameters; a `polygon` query keeps only the incidents that intersect it.\n */\nexport const parseTrafficIncidentDetailsResponse = (\n apiResponse: IncidentDetailsResponseAPI,\n params?: TrafficIncidentDetailsParams,\n): TrafficIncidentDetails => {\n const incidents = (apiResponse.incidents ?? [])\n .filter((incident): incident is IncidentAPI => incident !== null)\n .map(parseIncident);\n return {\n type: 'FeatureCollection',\n features: params?.polygon ? incidentsIntersecting(incidents, params.polygon) : incidents,\n };\n};\n","import { trafficIncidentRequestCategories } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { hasBBoxSchema } from '../shared/schema/geometriesSchema';\nimport type { SchemaRefinement } from '../shared/types/validation';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\nconst positionSchema = z.array(z.number()).min(2, 'a position needs at least two coordinates');\nconst linearRingSchema = z.array(positionSchema).min(4, 'a linear ring needs at least four positions');\nconst polygonCoordinatesSchema = z.array(linearRingSchema).min(1, 'a Polygon needs at least one ring');\n\nconst polygonGeometrySchema = z.discriminatedUnion('type', [\n z.object({\n type: z.literal('Polygon'),\n coordinates: polygonCoordinatesSchema,\n }),\n z.object({\n type: z.literal('MultiPolygon'),\n coordinates: z.array(polygonCoordinatesSchema).min(1, 'a MultiPolygon needs at least one polygon'),\n }),\n]);\n\nconst polygonFeatureSchema = z.object({\n type: z.literal('Feature'),\n geometry: polygonGeometrySchema,\n id: z.union([z.string(), z.number()]).optional(),\n properties: z.unknown().optional(),\n});\n\n// Polygonal shapes only: the response is trimmed with turf predicates that reject any other geometry. An embedded\n// `bbox` is not validated because neither the request nor the trim reads it.\nconst polygonSchema = z.union([\n polygonGeometrySchema,\n polygonFeatureSchema,\n z.object({\n type: z.literal('FeatureCollection'),\n features: z.array(polygonFeatureSchema).min(1, 'a FeatureCollection polygon needs at least one feature'),\n }),\n]);\n\n/**\n * @ignore\n */\nexport const trafficIncidentDetailsRequestSchema = commonServiceRequestSchema.extend({\n bbox: hasBBoxSchema.optional(),\n polygon: polygonSchema.optional(),\n ids: z.array(z.string()).min(1, 'ids must contain at least one id').optional(),\n categoryFilter: z.array(z.enum(trafficIncidentRequestCategories)).optional(),\n timeValidityFilter: z.array(z.enum(['present', 'future'])).optional(),\n});\n\nconst oneQueryModeRefinement: SchemaRefinement<TrafficIncidentDetailsParams> = {\n check: (data) => [data.bbox, data.polygon, data.ids].filter(Boolean).length === 1,\n message: 'Provide exactly one of bbox, polygon or ids',\n};\n\n/**\n * @ignore\n */\nexport const trafficIncidentDetailsRequestValidationConfig = {\n schema: trafficIncidentDetailsRequestSchema,\n refinements: [oneQueryModeRefinement],\n};\n","import type { TrafficIncidentDetails } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { buildTrafficIncidentDetailsRequest } from './requestBuilder';\nimport { parseTrafficIncidentDetailsResponse } from './responseParser';\nimport { trafficIncidentDetailsRequestValidationConfig } from './trafficIncidentDetailsRequestSchema';\nimport type { IncidentDetailsResponseAPI } from './types/apiTypes';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\ntype TrafficIncidentDetailsPostBody = { ids: string[] };\n\n/**\n * Traffic Incident Details service template type.\n * @ignore\n */\nexport type TrafficIncidentDetailsTemplate = ServiceTemplate<\n TrafficIncidentDetailsParams,\n FetchInput<TrafficIncidentDetailsPostBody>,\n IncidentDetailsResponseAPI,\n TrafficIncidentDetails\n>;\n\n/**\n * Traffic Incident Details service template main implementation.\n * @ignore\n */\nexport const trafficIncidentDetailsTemplate: TrafficIncidentDetailsTemplate = {\n requestValidation: trafficIncidentDetailsRequestValidationConfig,\n getAPIVersion: () => 2,\n buildRequest: buildTrafficIncidentDetailsRequest,\n sendRequest: fetchWith,\n parseResponse: parseTrafficIncidentDetailsResponse,\n};\n","import type { TrafficIncidentDetails } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { trafficIncidentDetailsTemplate } from './trafficIncidentDetailsTemplate';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\n/**\n * Fetch detailed information about traffic incidents.\n *\n * Query incidents by a geographic bounding box, by a polygon, or by a list of incident IDs.\n * Results are GeoJSON Features whose geometry is a `Point` (localised incidents) or\n * a `LineString` (incidents spanning a stretch of road).\n *\n * @remarks\n * **Three query modes:**\n *\n * - **Bounding box** (`bbox`): Returns all incidents within the given area.\n * Maximum area is 10,000 km².\n * - **Polygon** (`polygon`): Returns the incidents that intersect a polygon, such as a city boundary.\n * The request covers the polygon's bounding box, and the response is trimmed to the polygon.\n * - **By IDs** (`ids`): Returns the specified incidents directly.\n * GET is used automatically for up to 5 IDs; POST for up to 100 IDs.\n *\n * **Key data provided per incident:**\n * - Category (`category`): type of incident (accident, roadworks, jam, etc.)\n * - Delay magnitude: severity of the delay (`minor`, `moderate`, `major`, …)\n * - Geometry: exact location or extent of the incident on the road network\n * - Time information: start/end times and whether the incident is `present` or `future`\n * - Events: one or more detailed event descriptions\n *\n * @param params - Traffic Incident Details parameters (bbox, polygon or ids)\n *\n * @returns Promise resolving to a {@link TrafficIncidentDetails} object containing the matching incidents\n *\n * @example\n * ```typescript\n * // Query by bounding box (Amsterdam area)\n * const result = await trafficIncidentDetails({\n * bbox: [4.728, 52.278, 5.080, 52.479]\n * });\n *\n * result.features.forEach(incident => {\n * console.log(incident.properties.category); // 'accident' | 'jam' | …\n * console.log(incident.properties.magnitudeOfDelay); // 'minor' | 'major' | …\n * console.log(incident.geometry); // Point or LineString\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Query within a city boundary rather than its bounding box\n * const place = await geocodeOne('Amsterdam');\n * const result = await trafficIncidentDetails({\n * polygon: await geometryData({ geometries: [place] })\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Query up to 5 specific incidents by ID (GET)\n * const result = await trafficIncidentDetails({\n * ids: ['incident-id-1', 'incident-id-2']\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Query many incident IDs — POST is used automatically when ids.length > 5\n * const result = await trafficIncidentDetails({\n * ids: largeIdArray\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Filter by category and include future incidents\n * const result = await trafficIncidentDetails({\n * bbox: [4.728, 52.278, 5.080, 52.479],\n * categoryFilter: ['accident', 'road-closed'],\n * timeValidityFilter: ['present', 'future']\n * });\n * ```\n *\n * @see [Incident Details API Documentation](https://docs.tomtom.com/traffic-api/documentation/tomtom-orbis-maps/v2/traffic-incidents/incident-details)\n * @see [Incident Details Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/traffic/incident-details)\n *\n * @group Traffic\n */\nexport const trafficIncidentDetails = async (params: TrafficIncidentDetailsParams): Promise<TrafficIncidentDetails> =>\n callService(params, trafficIncidentDetailsTemplate, 'TrafficIncidentDetails');\n"],"mappings":"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GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GA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"}
1
+ {"version":3,"file":"services.es.js","names":["geometrySchema","buildUrlBasePath","buildUrlBasePath","buildUrlBasePath","buildUrlBasePath","buildUrlBasePath","buildRequestHeaders","DEFAULT_ATTRIBUTES","buildUrlBasePath","geometrySchema","buildUrlBasePath","geometrySchema","buildUrlBasePath","buildUrlBasePath","buildUrlBasePath","TomTomConfig","areaAnalyticsMetricKeys","avoidableTypes","bboxFromCoordsArray","bboxFromGeoJSON","bboxOnlyIfWithArea","connectorTypes","currentTypes","generateId","generateTomTomHeaders","geographyTypes","getPosition","getPositionStrict","getRoutePlanningLocationType","inputSectionTypesWithGuidance","isProxyCredentialsMode","mergeFromGlobal","plugTypes","poiCategories","poiCategoriesToID","poiIDsToCategories","toPointGeometry","trafficIncidentRequestCategories","views","z","countBy","isNil","maxBy","minBy","omit","partition","pick","uniqBy","bbox","booleanIntersects","APICode","SDKError","Error","constructor","message","service","issues","super","this","captureStackTrace","APIErrorCode","TOO_MANY_REQUESTS","FORBIDDEN","SDKServiceError","status","SDKAbortError","cause","name","parseDefaultResponseError","error","serviceName","data","errorText","buildResponseError","parseResponseError","signal","aborted","reason","fetchError","buildValidationError","commonServiceRequestSchema","object","apiKey","string","optional","commonBaseURL","language","formatZodIssue","issue","depth","path","length","join","code","unionErrors","map","unionError","i","nested","ValidationError","zodError","JSON","stringify","callService","async","params","template","mergedParams","apiVersion","validateRequest","config","validation","schema","extend","shape","superRefine","ctx","addIssue","refinements","refinement","check","safeParse","success","validateRequestSchema","requestValidation","e","apiRequest","buildRequest","headers","onAPIRequest","apiResponse","sendRequest","onAPIResponse","parseResponse","FetchError","returnOrThrow","response","ok","json","errorBody","contentType","get","bodyUsed","statusText","includes","detailedError","text","sleep","ms","Promise","resolve","reject","onAbort","clearTimeout","timer","setTimeout","removeEventListener","addEventListener","once","fetchWithRetry","fetchFn","retryConfig","instance","retry","initialWaitMs","backoffFactor","timeoutMs","startTime","Date","now","backoffMs","retryAfterSeconds","Number","parseInt","waitMs","isFinite","catch","proxyModeCredentials","input","sdkHeaders","options","credentials","url","inputHeaders","URL","init","fetch","post","method","body","fetchWith","lineStringCoordsSchema","array","number","geometrySchema$1","type","enum","coordinates","union","radius","radiusMeters","refine","Boolean","featureSchema","literal","geometry","id","properties","unknown","featureCollectionSchema","features","hasLngLatSchema","tuple","min","max","geoJsonbBoxSchema","arr","GeoJSONSchema","hasBBoxSchema","commonPlacesParamsSchema","query","position","limit","extendedPostalCodesFor","mapcodes","view","geographicFiltersSchema","countries","placeFiltersSchema","poiBrands","indexes","connectors","fuelTypes","minPowerKW","maxPowerKW","searchFiltersSchema","searchExtraParamsOptional","filters","openingHours","timeZone","relatedPois","minFuzzyLevel","maxFuzzyLevel","commonSearchParamsSchema","lineStringSchema","routeFeatureSchema","positionsArraySchema","routeSchema","alongRouteSearchRequestMandatory","route","maxDetourTimeSeconds","int","positive","alongRouteSearchRequestOptional","sortBy","alongRouteSearchRequestSchema","arrayToCSV","Array","isArray","String","sampleWithinMaxLength","maxLength","sampledArray","increment","Math","ceil","push","buildCredentialHeaders","buildCommonServiceRequestHeaders","appendCommonParams","urlParams","append","buildPlacesRequestHeaders","appendPlacesLanguageParam","appendEVModelVersionParam","appendByJoiningParamValue","values","appendOptionalParam","value","appendGeoBiasParams","geoBias","hasLngLat","appendLatLonParamsFromPosition","boundingBox","appendCommonSearchParams","searchUrl","searchParams","category","resolvePlacesEndpointUrl","endpoint","toChargingPointAvailability","chargingStations","availability","count","statusCounts","station","chargingPoint","chargingPoints","areEqual","connectorA","connectorB","ratedPowerKW","addConnectorCount","counts","connector","existingCount","find","connectorCount","addConnectorCountAndStatus","availabilities","existingAvailability","connectorAvailability","toConnectorCounts","toConnectorBasedAvailabilities","toChargingSpeed","powerInKW","toChargingStations","stations","capabilities","restrictions","chargingSpeed","csvLatLngToPosition","csv","splitLatLng","split","positionToCSVLatLon","apiToGeoJSONBBox","apiBBox","westSouth","eastNorth","topLeftPoint","hasTopLeftPoint","lon","btmRightPoint","lat","southWest","northEast","latLonAPIToPosition","point","searchCursorStateSchema","ofs","nonnegative","CURSOR_ERROR_MESSAGE","readCursorState","cursor","parse","atob","parseCursor","searchCursorSchema","appendSearchCursorParam","state","decodeSearchCursor","parseMoment","momentApi","year","month","day","dateYyyymmdd","splitDate","parseYyyymmddDate","date","dateYYYYMMDD","hour","minute","alwaysOpenInThisPeriod","timeRanges","start","end","parseOpeningHours","openingHoursApi","timeRangeApi","endTime","mode","alwaysOpenThisPeriod","parseSearchAPIResult","result","entryPoints","poi","dist","chargingStationsAPI","rest","distance","entrypoint","chargingPark","categories","categorySet","c","filter","localizedCategories","phone","brands","brand","parseSummaryAPI","summary","nextCursor","paginated","offset","numResults","totalResults","nextOffset","btoa","encodeSearchCursor","alongRouteSearchTemplate","encodeURIComponent","buildUrlBasePath$10","points","coord","results","alongRouteSearch","evChargingStationsAvailabilityRequestSchema","notCurrent","current","createLatestRequest","controller","cancel","abort","run","request","ownController","AbortController","isRunning","routeTypes","tollTransponderOptions","evChargingStationsAvailabilityTemplate","buildUrlBasePath$9","accessType","chargingPointAvailability","connectorAvailabilities","apiError","errorMessage","evChargingStationsAvailability","getPlaceWithEVAvailability","place","commonParams","availabilityId","dataSources","chargingAvailability","console","getPlacesWithEVAvailability","places","excludeIfAvailabilityUnknown","enhancedPlaces","placeWithAvailability","hasChargingAvailability","fetchFirstPlace","functionName","queryOrParams","first","pointGeoBiasSchema","geoBiasSchema","commonGeocodeAndFuzzySearchParamsSchema","typeahead","boolean","geocodingFiltersSchema","geocodingRequestSchema","parseApiResult","addressRanges","entityType","geographyType","from","to","geocodingTemplate","buildUrlBasePath$8","geocode","geocodeOne","geometryDataRequestMandatory","geometries","geometryDataRequestOptional","zoom","geometryDataRequestSchema","getGeometryIDs","placesArray","geometryDataTemplate","buildUrlBasePath$7","geometryIDs","appendGeometries","additionalData","flatMap","geometryData","feature","geometryResult","placesIdMap","reduce","acc","geometryId","placeCoordinates","mergePlacesWithGeometries","placeDetailsRequestMandatory","entityId","placeDetailsRequestOptional","placeDetailsRequestSchema","placeDetailsTemplate","buildUrlBasePath$6","getPlaceDetails","cache","Map","textCache","getWithDefault","normalizeText","toLowerCase","replaceAll","buildTextCache","texts","synonyms","poiCategoriesRequestSchema","parsePoiCategory","api","childCategoryCodes","childCategoryIds","poiCategoriesTemplate","baseUrl","filterCategories","cachedTextEntries","getCachedTextEntries","resultsByCode","filterTerm","normalizedFilterInput","Object","entries","set","childCode","delete","getPOICategories","allCategories","getCachedCategories","lang","setCachedCategories","getPOICategoryCodes","parseRoutingResponseError","description","kWhFromPercentage","maxChargeKWH","percentage","isElectricVehicle","vehicle","engineType","electricEngineOf","model","engine","withParameter","parameters","speedToConsumptionString","rates","rate","speedKMH","consumptionUnitsPer100KM","efficiencyParams","efficiency","acceleration","deceleration","uphill","downhill","electricConsumptionParams","consumption","speedsToConsumptionsKWH","constantSpeedConsumptionInkWhPerHundredkm","auxiliaryPowerInkW","consumptionInKWHPerKMAltitudeGain","recuperationInKWHPerKMAltitudeLoss","combustionConsumptionParams","speedsToConsumptionsLiters","constantSpeedConsumptionInLitersPerHundredkm","auxiliaryPowerInLitersPerHour","fuelEnergyDensityInMJoulesPerLiter","maxChargeKWHOf","charging","maxChargeParams","maxChargeInkWh","currentChargeKWHOf","absoluteCharge","currentChargeInkWh","currentChargePCT","currentChargeParams","currentFuelParams","currentFuelInLiters","chargingPreferencesOf","preferences","chargingPreferences","setEnergyParams","positiveNumber","nonNegativeNumber","percentageNumber","optionalPositiveNumber","optionalNonNegativeNumber","optionalNormalizedNumber","speedToConsumptionRateSchema","baseConsumptionModelSchema","speedConsumptionArray","combustionConsumptionModelSchema","electricConsumptionModelSchema","chargingConnectorSchema","currentType","baseLoadInkW","maxPowerInkW","maxVoltageInV","maxCurrentInA","voltageRange","minVoltageInV","batteryCurveSchema","stateOfChargeInkWh","chargingModelSchema","batteryCurve","chargingConnectors","chargingTimeOffsetInSec","combustionEngineModelSchema","electricEngineModelSchema","vehicleDimensionsSchema","weightKG","predefinedVehicleModelSchema","variantId","explicitVehicleModelSchema","dimensions","vehicleModelSchema","genericVehicleStateSchema","heading","combustionVehicleStateSchema","electricVehicleStateByPercentageSchema","electricVehicleStateByKwhSchema","electricVehicleStateSchema","chargingPreferencesPCTSchema","minChargeAtDestinationPCT","minChargeAtChargingStopsPCT","chargingPreferencesKWHSchema","minChargeAtDestinationInkWh","minChargeAtChargingStopsInkWh","chargingPreferencesSchema","electricVehiclePreferencesSchema","baseVehicleParamsSchema","maxSpeedKMH","tollTransponder","genericVehicleParamsSchema","undefined","combustionVehicleParamsSchema","electricVehicleParamsSchema","vehicleParametersSchema","discriminatedUnion","costModelSchema","avoid","traffic","routeType","avoidAreas","departArriveSchema","option","commonRoutingRequestSchema","costModel","travelMode","when","budgetTypes","reachableRangeRequestSchemaMandatory","origin","budget","reachableRangeAvoidableSchema","exclude","reachableRangeVehicleSchema","reachableRangeWhenSchema","reachableRangeCostModelSchema","reachableRangeRequestSchemaOptional","maxFerryLengthMeters","smoothing","reachableRangeRequestSchema","chargeBudgetTypes","Set","batteryCapacityRefinement","has","currentChargeRefinement","reachableRangeRequestValidationConfig","buildCommonRoutingBody","avoids","departureDateTime","toISOString","predefinedModelIdOf","buildCommonVehicleBody","vehicleEngineType","vehicleWeightInKilograms","vehicleMaxSpeedInKilometersPerHour","vehicleHasElectronicTollCollectionTransponder","appendVehicleEnergyParams","electricEngine","combustionEngine","combustionEngineOf","appendVehicleQueryParams","buildPostData","buildRequestHeaders$1","Attributes","propertyKeys","keys","reachableRangeTemplate","buildUrl","toString","currentChargeKWH","remainingKWH","appendBudget","reachableRange","boundary","longitude","latitude","calculateReachableRange","combineSignals","signals","live","AbortSignal","any","calculateReachableRanges","paramsArray","AREA_TYPE_BY_GEOGRAPHY_TYPE","Country","CountrySubdivision","CountrySecondarySubdivision","CountryTertiarySubdivision","Municipality","MunicipalitySubdivision","MunicipalitySecondarySubdivision","Neighbourhood","PostalCodeArea","ISO2_TO_ISO3","AF","AX","AL","DZ","AS","AD","AO","AI","AQ","AG","AR","AM","AW","AU","AT","AZ","BS","BH","BD","BB","BY","BE","BZ","BJ","BM","BT","BO","BQ","BA","BW","BV","BR","IO","BN","BG","BF","BI","CV","KH","CM","CA","KY","CF","TD","CL","CN","CX","CC","CO","KM","CG","CD","CK","CR","CI","HR","CU","CW","CY","CZ","DK","DJ","DM","DO","EC","EG","SV","GQ","ER","EE","SZ","ET","FK","FO","FJ","FI","FR","GF","PF","TF","GA","GM","GE","DE","GH","GI","GR","GL","GD","GP","GU","GT","GG","GN","GW","GY","HT","HM","VA","HN","HK","HU","IS","IN","ID","IR","IQ","IE","IM","IL","IT","JM","JP","JE","JO","KZ","KE","KI","KP","KR","KW","KG","LA","LV","LB","LS","LR","LY","LI","LT","LU","MO","MG","MW","MY","MV","ML","MT","MH","MQ","MR","MU","YT","MX","FM","MD","MC","MN","ME","MS","MA","MZ","MM","NA","NR","NP","NL","NC","NZ","NI","NE","NG","NU","NF","MK","MP","NO","OM","PK","PW","PS","PA","PG","PY","PE","PH","PN","PL","PT","PR","QA","RE","RO","RU","RW","BL","SH","KN","LC","MF","PM","VC","WS","SM","ST","SA","SN","RS","SC","SL","SG","SX","SK","SI","SB","SO","ZA","GS","SS","ES","LK","SD","SR","SJ","SE","CH","SY","TW","TJ","TZ","TH","TL","TG","TK","TO","TT","TN","TR","TM","TC","TV","UG","UA","AE","GB","US","UM","UY","UZ","VU","VE","VN","VG","VI","WF","EH","YE","ZM","ZW","toIso3","iso2","PLACE_TYPE_BY_RESULT_TYPE","street","area","address","GEOGRAPHY_TYPE_BY_AREA_TYPE","fromEntries","areaType","mapAddress","apiAddress","houseNumber","countryCodeIso2","streetName","streetNumber","countryCode","countryCodeISO3","revGeocodeRequestMandatory","revGeocodeRequestOptional","revGeocodeRequestSchema","reverseGeocodingTemplate","lng","buildUrlBasePath$5","firstResult","freeformAddress","title","accessPoints","accessPoint","originalPosition","reverseGeocode","EXPLICIT_SECTION_TYPES","buildAttributesHeader","parts","sectionTypes","wantAll","guidance","extendedRouteRepresentations","routesAttr","buildVehicleBody","vehicleHeadingInDegrees","appendLDEVRVehicleParams","absolutePreferences","percentagePreferences","chargingPreferenceParams","isLDEVRRequest","PREDEFINED_MODEL_WITHOUT_LDEVR_MESSAGE","expandPathToStops","pathLike","getPathCoordinates","legSections","sections","leg","getPathLegSections","every","section","startPointIndex","endPointIndex","stops","coordinate","legToNext","slice","at","getRouteStopOptions","location","toLegRequest","arrivingStop","routeStop","pauseDurationSeconds","pauseDurationInSeconds","candidateEntryPoints","entryPoint","preferredEntryPointIndex","legCostModel","appendTravelTimeParam","computeTravelTimeFor","waypointLikeSchema","pathLikeSchema","mandatorySchema","locations","optionalSchema","arrivalSide","chargingStopsStrategy","maxAlternatives","routeRequestValidationConfig","types","waypointCount","pathCount","destination","iconCategoryByCategory","other","accident","fog","danger","rain","frost","jam","roadworks","wind","flooding","iconCategoryLookup","categoryByIconCategory","delayMagnitudeLookup","minor","moderate","major","toIconCategory","toTrafficIncidentCategory","iconCategory","toDelayMagnitude","apiMagnitude","toCurrentType","apiCurrentType","toChargingStop","chargingInformationAtEndOfLeg","chargingConnectionInfo","chargingParkLocation","addressParts","chargingParkId","chargingParkUuid","city","municipality","region","countrySubdivision","postalCode","country","plugType","chargingPlugType","currentInA","chargingCurrentInA","voltageInV","chargingVoltageInV","chargingPowerInkW","chargingCurrentType","targetChargeInPCT","targetChargeInkWh","chargingParkPowerInkW","chargingParkSpeed","parseSummary","apiSummary","lengthInMeters","travelTimeInSeconds","travelDurationInSeconds","trafficDelayInSeconds","trafficDelayDurationInSeconds","trafficLengthInMeters","historicTrafficTravelTimeInSeconds","liveTrafficIncidentsTravelTimeInSeconds","noTrafficTravelTimeInSeconds","departureTime","arrivalTime","arrivalDateTime","deviationDistanceInMeters","deviationTimeInSeconds","deviationDurationInSeconds","deviationPoint","fuelConsumptionInLiters","totalChargingTimeInSeconds","batteryConsumptionInkWh","batteryConsumptionInKilowattHours","batteryConsumption","batteryConsumptionInPCT","remainingChargeAtArrivalInkWh","remainingChargeAtArrivalInPCT","parseLegSectionProps","apiLegs","accumulator","legIndex","legCoordinateCount","lastEndIndex","stopTimeInSeconds","nextLeg","arrival","getTime","nextDeparture","isNaN","stopSeconds","round","parseLegStopTime","originalWaypointIndexAtEndOfLeg","originalWaypointIndex","toSectionProps","startPathIndex","endPathIndex","parseSections","apiRoute","legs","addBasic","key","items","carTrain","ferry","tunnel","motorway","pedestrian","toll","tollRoad","carpool","urban","unpaved","lowEmissionZone","countryCodeISO2","vehicleRestricted","travelModeSection","delayInSeconds","delayDurationInSeconds","effectiveSpeedInKmh","effectiveSpeedInKilometersPerHour","tec","causes","mainCauseCode","magnitudeOfDelay","delayMagnitude","eventId","speedLimit","speedRestrictions","r","inKilometersPerHour","maxSpeedLimitInKmh","lanes","lanesSection","lane","directions","direction","LANE_DIRECTION_MAP","follow","laneSeparators","separator","LANE_SEPARATOR_MAP","roadShields","roadShield","roadShieldReferences","tollVignette","importantRoadStretch","index","importantRoadStretchIndex","roadNumbers","roadNumber","requested","applyRequestedSectionFilter","similar","pointA","pointB","abs","MANEUVER_MAP","depart","waypointLeft","waypointRight","waypointReached","waypointAhead","arrive","arriveLeft","arriveRight","arriveAhead","continueStraight","keepLeft","keepRight","turnSlightRight","turnRight","turnSharpRight","turnSlightLeft","turnLeft","turnSharpLeft","makeUTurn","roundaboutStraight","roundaboutSharpRight","roundaboutRight","roundaboutSlightRight","roundaboutSlightLeft","roundaboutLeft","roundaboutSharpLeft","roundaboutBack","exitRoundabout","exitMotorwayLeft","exitMotorwayRight","switchMotorwayLeft","switchMotorwayRight","enterCarpoolLaneLeft","enterCarpoolLaneRight","exitCarpoolLaneLeft","exitCarpoolLaneRight","mergeLeftLane","mergeRightLane","takeShipFerry","takeCarTrain","leaveShipFerry","leaveCarTrain","crossBorder","passTollgate","ROAD_PROPERTY_MAP","controlledAccess","LANDMARK_MAP","endOfRoad","atTrafficLight","onToBridge","onBridge","afterBridge","intoTunnel","insideTunnel","afterTunnel","TOLL_PAYMENT_MAP","cashCoinsAndBills","cashBillsOnly","cashCoinsOnly","cashExactChange","creditCard","debitCard","travelCard","etc","etcTransponder","etcVideoCamera","subscription","straight","slightRight","right","sharpRight","rightUTurn","slightLeft","left","sharpLeft","leftUTurn","noMarking","longDashed","doubleSolid","singleSolid","solidDashed","dashedSolid","shortDashed","shadedAreaMarking","dashedBlocks","doubleDashed","crossingAlert","physicalDivider","physicalDividerLessThan3m","physicalDividerGuardrail","curb","MANEUVER_ANGLE_MAP","back","ROUNDABOUT_TYPE_MAP","regular","small","SIDE_ROAD_SIDE_MAP","leftAndRight","EMPTY_ROAD_INFO","toInstructionText","instructionText","parseInstructionRoadInformation","road","p","toUpperCase","roadNames","identifier","s","countrySubdivisionCodeIso","stateCode","iconReference","roadShieldReference","parseTollAndCrossing","a","tollgateName","tollPaymentTypes","t","countryCrossingFromName","countryCrossingFromCodeIso2","countryCrossingFromCode","countryCrossingToName","countryCrossingToCodeIso2","countryCrossingToCode","toManeuverAngle","angle","parseManeuverExtras","intersectionName","drivingSide","landmark","distanceToPreviousTrafficLightInMeters","trafficLightOffsetInMeters","sideRoads","sideRoad","side","offsetFromManeuverInMeters","isDrivable","maneuverView","onRouteAngle","offRouteAngles","roundaboutType","isManeuverObligatory","changeOfAngleInDegrees","ambiguousExitOffsetFromManeuverInMeters","offsetOfAmbiguousExitFromManeuverInMeters","roundaboutExitNumber","parseRoadShieldFields","roadShieldAtlasReference","signpost","exitName","exitNumber","towardName","parseSignpost","nextRoadInformation","ref","exitIconReference","signpostRoadShieldReferences","parseInstruction","maneuverPoint","pathPointIndex","routeOffsetInMeters","maneuver","routePath","rp","distanceInMeters","distanceFromRouteStartInMeters","travelTimeFromRouteStartInSeconds","nextRoadInfo","previousRoadInfo","previousRoadInformation","parseGuidance","instructions","parsed","lastPathIndex","apiInstruction","pathIndex","calculateRouteTemplate","hasPathLocations","allStops","useEntryPoint","stopEntryPoint","flattenLocations","useEntryPoints","routePlanningLocations","middleStops","waypoints","stop","buildRoutePlanningLocations","commonBodyFields","maxPathAlternativeRoutes","instructionPhonetics","rectangles","rectangle","minLon","minLat","maxLon","maxLat","arrivalSidePreference","buildCommonBodyFields","buildRequestHeaders","buildLegs","sharedBody","ldevrRequest","chargingModel","chargingParameters","buildLDEVRRequest","routes","progressPoints","progress","progressPoint","pointIndex","parseRoute","calculateRoute","fuzzySearchRequestOptional","fuzzySearchRequestSchema","queryIntentApiToSdk","intentApi","intent","details","fuzzySearchTemplate","buildUrlBasePath$4","queryIntent","geometrySearchRequestMandatory","geometrySearchRequestSchema","sdkGeometryToApiGeometries","searchGeometry","vertices","polygonSizeMap","forEach","polygon","bboxOfPolygon","polygonSize","sort","b","splice","findFiftyLargestPolygons","polygonCoords","geometrySearchTemplate","mergedOptions","geometryList","discoverPlaces","geometrySearch","fuzzySearch","discoverOnePlace","autocompleteSearchRequestMandatory","autocompleteSearchRequestOptional","resultTypes","autocompleteSearchRequestSchema","autocompleteSearchTemplate","context","segments","segment","isCategorySegment","autocompleteSearch","words","normalize","replace","namesOf","matchedAlternativeName","findConceptSegment","candidates","some","target","nameWords","_","startsWith","startsAtWordOf","queryWord","nameWord","coversMostOfWordIn","blendSuggestions","concept","maxResults","distinctPlaces","ordered","titleWords","titleWord","titleMatches","flat","suggestion","REFINEMENT_KINDS","isRefinementKind","kind","INDEXES_BY_PLACE_KIND","KIND_BY_PLACE_TYPE","POI","Street","Geography","toRadians","degrees","PI","distanceFrom","lng1","lat1","lng2","lat2","halfChord","sin","cos","asin","sqrt","metersBetween","toPlaceSuggestions","originPosition","homeCountryCode","homeCountryCodeOf","addressLines","subtitles","foreignCountry","toPlaceSuggestion","throwAsSearchSuggestionsError","toRefinement","scope","narrowing","refineParams","shared","toRefinementSuggestions","pickConcept","searchCarriesBrand","getSearchSuggestions","placeKinds","refinementKinds","wantsPlaces","wantsRefinements","kinds","splitKinds","originBias","placesBias","poiTypes","areaTypes","searchResponse","autocompleteResponse","all","autocompleteResults","suggestions","addDays","days","setUTCDate","getUTCDate","daysAgo","toDateString","startOfDayUTC","OFFSET","parseDateTime","dateTime","test","functionalRoadClasses","API_NAMES_BY_METRIC_KEY","speed","dataType","field","freeFlowSpeed","congestionLevel","travelTime","networkLength","ALL_HOURS","frcToNumber","frc","indexOf","parseMetricFields","parseValue","metricKey","apiValue","parseMetrics","parseMetricKey","parsePeriodStart","time","getUTCFullYear","getUTCMonth","getUTCHours","IDENTIFIERS_BY_GRANULARITY","yearly","monthly","weekly","thursday","getUTCDay","dayOfYear","UTC","week","floor","parseISOWeek","daily","hourly","parseCalendarEntries","granularity","entry","DAY_BY_API_DAY_OF_WEEK","MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY","parseAverageEntry","apiDay","hasOwn","hourOfWeek","parseTileEntry","tile","tileCentre","parseAnomaly","startDate","endDate","labels","parseTimedData","average","second","parseAnomalies","anomalies","parseFeature","apiFeature","timezone","level","baseData","timedData","tiledData","tiles","computeTileRanges","f","ranges","v","parseAnalysisPeriod","times","dateInputSchema","regex","geometrySchema","isAccepted","trafficAreaAnalyticsTemplate","metrics","hours","hasDays","hasStart","diffDays","buildUrlBasePath$1","datePart","dataTypes","frcs","heatmap","trafficAreaAnalytics","DEFAULT_ATTRIBUTES","timeValidity","probabilityOfOccurrence","numberOfReports","lastReportTime","property","parseEvent","apiEvent","parseIncident","apiIncident","events","withoutBBox","incidentsIntersecting","incidents","toGeometries","incident","incidentBBox","part","bboxesIntersect","positionSchema","linearRingSchema","polygonCoordinatesSchema","polygonGeometrySchema","polygonFeatureSchema","polygonSchema","trafficIncidentDetailsTemplate","ids","categoryFilter","timeValidityFilter","buildUrlBasePath","appendOptionalParams","buildHeaders","resolvedBBox","recompute","trafficIncidentDetails"],"sources":["../src/shared/types/apiResponseErrorTypes.ts","../src/shared/errors.ts","../src/shared/schema/commonParamsSchema.ts","../src/shared/schema/validation.ts","../src/shared/serviceTemplate.ts","../src/shared/fetch.ts","../src/shared/schema/geometriesSchema.ts","../src/shared/schema/commonPlacesParamsSchema.ts","../src/shared/schema/searchFiltersSchema.ts","../src/search/commonSearchParamsSchema.ts","../src/along-route-search/alongRouteSearchRequestSchema.ts","../src/shared/arrays.ts","../src/shared/request/requestBuildingUtils.ts","../src/shared/request/commonSearchRequestBuilder.ts","../src/shared/request/placesEndpoint.ts","../src/along-route-search/requestBuilder.ts","../src/ev-charging-stations-availability/connectorAvailability.ts","../src/shared/ev.ts","../src/shared/geometry.ts","../src/shared/searchCursor.ts","../src/shared/searchResultParsing.ts","../src/along-route-search/responseParser.ts","../src/along-route-search/alongRouteSearchTemplate.ts","../src/along-route-search/alongRouteSearch.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityRequestSchema.ts","../src/shared/latestRequest.ts","../src/shared/types/commonRoutingParams.ts","../src/shared/types/vehicleRestrictionParams.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityResponseErrorParser.ts","../src/ev-charging-stations-availability/requestBuilder.ts","../src/ev-charging-stations-availability/responseParser.ts","../src/ev-charging-stations-availability/evChargingStationsAvailabilityTemplate.ts","../src/ev-charging-stations-availability/evChargingStationsAvailability.ts","../src/shared/firstPlace.ts","../src/shared/schema/geoBiasSchema.ts","../src/shared/schema/commonGeocodeAndFuzzySearchParamsSchema.ts","../src/geocode/geocodingRequestSchema.ts","../src/geocode/requestBuilder.ts","../src/geocode/responseParser.ts","../src/geocode/geocodingTemplate.ts","../src/geocode/geocoding.ts","../src/geometry-data/geometryDataRequestSchema.ts","../src/geometry-data/requestBuilder.ts","../src/geometry-data/responseParser.ts","../src/geometry-data/geometryDataTemplate.ts","../src/geometry-data/geometryData.ts","../src/place-details/placeDetailsSchema.ts","../src/place-details/requestBuilder.ts","../src/place-details/responseParser.ts","../src/place-details/placeDetailsTemplate.ts","../src/place-details/getPlaceDetails.ts","../src/poi-categories/cache.ts","../src/poi-categories/poiCategoriesRequestSchema.ts","../src/poi-categories/requestBuilder.ts","../src/poi-categories/responseParser.ts","../src/poi-categories/poiCategoriesTemplate.ts","../src/poi-categories/poiCategories.ts","../src/routing/routingResponseErrorParser.ts","../src/shared/request/vehicleEnergyParams.ts","../src/shared/schema/vehicleParamsSchema.ts","../src/shared/schema/commonRoutingRequestSchema.ts","../src/reachable-range/types/reachableRangeParams.ts","../src/reachable-range/reachableRangeRequestSchema.ts","../src/shared/request/routingBodyBuilder.ts","../src/reachable-range/requestBuilder.ts","../src/reachable-range/responseParser.ts","../src/reachable-range/reachableRangeTemplate.ts","../src/reachable-range/calculateReachableRange.ts","../src/revgeo/requestBuilder.ts","../src/shared/iso2ToIso3.ts","../src/revgeo/responseParser.ts","../src/revgeo/revGeocodeRequestSchema.ts","../src/revgeo/reverseGeocodingTemplate.ts","../src/revgeo/reverseGeocoding.ts","../src/routing/requestBuilder.ts","../src/routing/types/calculateRouteParams.ts","../src/routing/calculateRouteRequestSchema.ts","../src/shared/trafficIncidents.ts","../src/routing/responseParser.ts","../src/routing/calculateRouteTemplate.ts","../src/routing/calculateRoute.ts","../src/fuzzy-search/fuzzySearchRequestSchema.ts","../src/fuzzy-search/requestBuilder.ts","../src/fuzzy-search/responseParser.ts","../src/fuzzy-search/fuzzySearchTemplate.ts","../src/fuzzy-search/fuzzySearch.ts","../src/geometry-search/geometrySearchRequestSchema.ts","../src/geometry-search/requestBuilder.ts","../src/geometry-search/responseParser.ts","../src/geometry-search/geometrySearchTemplate.ts","../src/geometry-search/geometrySearch.ts","../src/search/discoverPlaces.ts","../src/autocomplete-search/autocompleteSearchRequestSchema.ts","../src/autocomplete-search/requestBuilder.ts","../src/autocomplete-search/responseParser.ts","../src/autocomplete-search/autocompleteSearchTemplate.ts","../src/autocomplete-search/autocompleteSearch.ts","../src/search-suggestions/blend.ts","../src/search-suggestions/getSearchSuggestions.ts","../src/traffic-area-analytics/dates.ts","../src/traffic-area-analytics/types/trafficAreaAnalyticsParams.ts","../src/traffic-area-analytics/requestBuilder.ts","../src/traffic-area-analytics/responseParser.ts","../src/traffic-area-analytics/trafficAreaAnalyticsRequestSchema.ts","../src/traffic-area-analytics/trafficAreaAnalyticsTemplate.ts","../src/traffic-area-analytics/trafficAreaAnalytics.ts","../src/traffic-incident-details/requestBuilder.ts","../src/traffic-incident-details/responseParser.ts","../src/traffic-incident-details/trafficIncidentDetailsRequestSchema.ts","../src/traffic-incident-details/trafficIncidentDetailsTemplate.ts","../src/traffic-incident-details/trafficIncidentDetails.ts"],"sourcesContent":["/**\n * @ignore\n */\nexport interface BaseAPIResponseErrorBody {\n /*\n * HTTP error code.\n */\n httpStatusCode: number;\n /*\n * Detailed information about the error.\n */\n detailedError: {\n /*\n * One of the defined error codes.\n */\n code: string;\n /*\n * A human-readable representation of the error code.\n */\n message: string;\n /*\n * Target of the particular error.\n * Value: The name of the request parameter.\n */\n target: string;\n };\n}\n\n/**\n * @ignore\n */\nexport interface DefaultAPIResponseErrorBody extends BaseAPIResponseErrorBody {\n /*\n * A human-readable description of the error code.\n */\n error?: string;\n errorText?: string;\n}\n\n/**\n * @ignore\n */\nexport interface RoutingAPIResponseError {\n /*\n * \tThe format version\n */\n formatVersion: string;\n error: {\n /*\n * A human-readable representation of the error code.\n */\n description: string;\n };\n detailedError: {\n /*\n * A human-readable representation of the error code.\n */\n message: string;\n /*\n * One of the defined error codes.\n */\n code: string;\n };\n}\n\n/**\n * Error returned by an API.\n * @ignore\n */\nexport type APIErrorResponse<T = DefaultAPIResponseErrorBody> = {\n status?: number;\n message: string;\n data?: T;\n};\n\n/**\n * @ignore\n */\nexport enum APICode {\n TOO_MANY_REQUESTS = 429,\n FORBIDDEN = 403,\n}\n","import type { ZodIssue } from 'zod';\nimport type { ValidationError } from './schema/validation';\nimport type { ParseResponseError } from './serviceTypes';\nimport type { APIErrorResponse, DefaultAPIResponseErrorBody } from './types/apiResponseErrorTypes';\nimport { APICode } from './types/apiResponseErrorTypes';\nimport type { ServiceName } from './types/servicesTypes';\n\n/**\n * Base error class for all SDK-related errors.\n *\n * The SDK handles two distinct categories of errors:\n * 1. **Programming errors**: Configuration or usage errors in the user's application,\n * such as passing incorrect types to parameters or functions.\n * 2. **API errors**: Recoverable errors that occur during SDK operations,\n * such as network failures or invalid API responses.\n *\n * @example\n * ```typescript\n * try {\n * // SDK operation\n * } catch (error) {\n * if (error instanceof SDKError) {\n * console.error(`Error in ${error.service}: ${error.message}`);\n * }\n * }\n * ```\n *\n * @group Errors\n */\nexport class SDKError extends Error {\n /**\n * Creates a new SDKError instance.\n *\n * @param message - Human-readable error description\n * @param service - Name of the service where the error occurred\n * @param issues - Optional array of Zod validation issues for detailed error information\n */\n constructor(\n message: string,\n readonly service: string,\n readonly issues?: ZodIssue[],\n ) {\n super(message);\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SDKError);\n }\n }\n}\n\n/**\n * Mapping of API error codes to human-readable error messages.\n *\n * Provides standardized error messages for common API response codes,\n * ensuring consistent error handling across the SDK.\n *\n * @group Errors\n */\nexport const APIErrorCode: { readonly [K in APICode as number]: string } = {\n [APICode.TOO_MANY_REQUESTS]: 'Too Many Requests: The API Key is over QPS (Queries per second)',\n [APICode.FORBIDDEN]: 'Request failed with status code 403',\n};\n\n/**\n * Error class for HTTP API response errors.\n *\n * Extends {@link SDKError} to include HTTP status codes and automatically\n * maps known error codes to user-friendly messages using {@link APIErrorCode}.\n *\n * @example\n * ```typescript\n * throw new SDKServiceError('Invalid request', 'geocoding', 400);\n * ```\n *\n * @group Errors\n */\nexport class SDKServiceError extends SDKError {\n /**\n * HTTP status code of the failed API request.\n *\n * Common values:\n * - `400`: Bad Request\n * - `403`: Forbidden\n * - `429`: Too Many Requests\n * - `500`: Internal Server Error\n */\n status?: number;\n\n /**\n * Creates a new SDKServiceError instance.\n *\n * If the status code matches a known error in {@link APIErrorCode},\n * the message will be automatically replaced with the standardized message.\n *\n * @param message - Error message from the API or custom message\n * @param service - Name of the service that generated the error\n * @param status - HTTP status code of the failed request\n */\n constructor(message: string, service: string, status?: number) {\n super(message, service);\n this.status = status;\n\n /*\n * We use as message what returns from API if any, otherwise we have our APIErrorCode as a fallback\n * Check if there is a status and if the status exists in the mapped API error types\n */\n if (this.status && APIErrorCode[this.status]) {\n this.message = APIErrorCode[this.status];\n }\n }\n}\n\n/**\n * Error class for cancelled service calls.\n *\n * Thrown when the `signal` passed to a service call is aborted, either before the\n * request is sent or while it is in flight. Its `name` is `'AbortError'`, matching\n * the convention used by `fetch`, and the original abort reason — including a custom\n * value passed to `AbortController.abort(reason)` — is available on `cause`.\n *\n * To drop a call a newer one supersedes without catching this error, run it through\n * {@link createLatestRequest}.\n *\n * @example\n * ```typescript\n * try {\n * await discoverPlaces({ query: 'cafe', signal: AbortSignal.timeout(5000) });\n * } catch (error) {\n * if (error instanceof SDKAbortError) return showMessage('Search timed out');\n * throw error;\n * }\n * ```\n *\n * @group Errors\n */\nexport class SDKAbortError extends SDKError {\n /**\n * Creates a new SDKAbortError instance.\n *\n * @param service - Name of the service whose call was cancelled\n * @param cause - The abort reason carried by the signal\n */\n constructor(service: string, cause?: unknown) {\n super('The operation was aborted.', service);\n this.name = 'AbortError';\n this.cause = cause;\n }\n}\n\n/**\n * @ignore\n * @param error\n * @param serviceName\n */\nexport const parseDefaultResponseError: ParseResponseError<DefaultAPIResponseErrorBody> = (error, serviceName) => {\n const { data, message, status } = error;\n // Different services uses property error or errorText or detailedError\n // Here we cover all situations as a default error parser\n const errorMessage = data?.error || data?.errorText || message;\n return new SDKServiceError(errorMessage, serviceName, status);\n};\n\n/**\n * @ignore\n * Generate error for APIResponse, any other error type will be returned as it is.\n * @param error The error captured by a catch function.\n * @param serviceName The name of the service.\n * @param parseResponseError\n * @param signal The signal given to the call, used to recognise a cancellation.\n */\nexport const buildResponseError = (\n error: unknown,\n serviceName: ServiceName,\n parseResponseError?: ParseResponseError<unknown>,\n signal?: AbortSignal,\n): SDKError => {\n // Checked before the status: an aborted fetch carries no status. Not a bare `signal?.aborted`\n // — `parseResponse` runs in the same `try`, so a real error thrown while the signal happens to\n // be aborted must not be reported as a cancellation. fetch rejects with exactly `signal.reason`.\n if (signal?.aborted && error === signal.reason) {\n return new SDKAbortError(serviceName, signal.reason);\n }\n\n // A custom `sendRequest` may mint its own AbortError instead.\n if (signal?.aborted && (error as Error)?.name === 'AbortError') {\n return new SDKAbortError(serviceName, signal.reason);\n }\n\n if ((error as APIErrorResponse).status) {\n const fetchError = error as APIErrorResponse;\n if (parseResponseError) {\n return parseResponseError(fetchError, serviceName);\n }\n return parseDefaultResponseError(fetchError, serviceName);\n }\n\n return new SDKError((error as Error).message, serviceName);\n};\n\n/**\n * @ignore\n * @param error\n * @param serviceName\n */\nexport const buildValidationError = (error: ValidationError, serviceName: ServiceName): SDKError =>\n new SDKError(error.message, serviceName, error.issues);\n","import { z } from 'zod';\n\n/**\n * @ignore\n */\nexport const commonServiceRequestSchema = z.object({\n apiKey: z.string().optional(),\n commonBaseURL: z.string().optional(),\n language: z.string().optional(),\n});\n","import type { ZodError } from 'zod';\nimport type { CommonServiceParams } from '../serviceTypes';\nimport type { RequestValidationConfig } from '../types/validation';\nimport { commonServiceRequestSchema } from './commonParamsSchema';\n\ntype ZodIssue = ZodError['issues'][number];\n\n/**\n * Format a Zod error into a human-readable string\n * @ignore\n */\nconst formatZodIssue = (issue: ZodIssue, depth = 0): string => {\n const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : '';\n if (issue.code === 'invalid_union') {\n const unionErrors = (issue as ZodIssue & { unionErrors?: ZodError[] }).unionErrors;\n if (unionErrors?.length) {\n const branches = unionErrors\n .map((unionError, i) => {\n const branchIssues = unionError.issues\n .map((nested) => formatZodIssue(nested, depth + 1))\n .join(', ');\n return `option[${i}]: { ${branchIssues} }`;\n })\n .join(' | ');\n return `${path}no matching union member (${branches})`;\n }\n }\n return `${path}${issue.message}`;\n};\n\nconst formatZodError = (error: ZodError): string => {\n return error.issues.map((issue) => formatZodIssue(issue)).join('; ');\n};\n\n/**\n * Validate Error Class for validating params input, this will be used by SDKError class.\n * @ignore\n */\nexport class ValidationError extends Error {\n issues: ZodError['issues'];\n\n constructor(zodError: ZodError) {\n const message = formatZodError(zodError);\n const issuesSummary = JSON.stringify(zodError.issues, null, 2);\n super(`${message}\\nIssues: ${issuesSummary}`);\n this.issues = zodError.issues;\n }\n}\n\n/**\n * @ignore\n * @param params\n * @param config\n */\nexport const validateRequestSchema = <T extends CommonServiceParams>(\n params: T,\n config?: RequestValidationConfig,\n): T => {\n const requestSchema = config?.schema\n ? commonServiceRequestSchema.extend(config.schema.shape)\n : commonServiceRequestSchema;\n\n // Apply all refinements using superRefine for better type compatibility in Zod v4\n const finalSchema = requestSchema.superRefine((data, ctx) => {\n if (!('commonBaseURL' in data)) {\n ctx.addIssue({ code: 'custom', message: 'commonBaseURL is required' });\n }\n\n // Apply optional refinements\n if (config?.refinements?.length) {\n for (const refinement of config.refinements) {\n if (!refinement.check(data as T)) {\n ctx.addIssue({\n code: 'custom',\n message: refinement.message,\n });\n }\n }\n }\n });\n\n const validation = finalSchema.safeParse(params);\n if (!validation.success) {\n throw new ValidationError(validation.error);\n }\n\n return params;\n};\n","import { generateTomTomHeaders, mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\nimport { buildResponseError, buildValidationError, SDKAbortError } from './errors';\nimport type { ValidationError } from './schema/validation';\nimport { validateRequestSchema } from './schema/validation';\nimport type { CommonServiceParams, ServiceTemplate } from './serviceTypes';\nimport type { ServiceName } from './types/servicesTypes';\n\nconst DEFAULT_API_VERSION = 1;\n\n/**\n * @ignore\n * Template execution of a service call.\n * Any service goes through the same template steps:\n * 1- Build request\n * 2- Send request and get API response\n * 3- Parse and return API response\n * @param params The parameters for that specific service call.\n * @param template The implementation of the template steps.\n * @param serviceName The name of the service.\n */\nexport const callService = async <PARAMS extends CommonServiceParams, ApiRequest, ApiResponse, RESPONSE>(\n params: PARAMS,\n template: ServiceTemplate<PARAMS, ApiRequest, ApiResponse, RESPONSE>,\n serviceName: ServiceName,\n): Promise<RESPONSE> => {\n // Bail before doing any work, so a superseded call never fires onAPIRequest\n if (params.signal?.aborted) {\n throw new SDKAbortError(serviceName, params.signal.reason);\n }\n\n // Spread last, so an untyped `apiVersion` from the caller or the global config never reaches the API\n const mergedParams = { ...mergeFromGlobal(params), apiVersion: template.apiVersion ?? DEFAULT_API_VERSION };\n // (params.validateRequest defaults to true, thus true and undefined are the same)\n if (params.validateRequest === undefined || params.validateRequest) {\n try {\n validateRequestSchema<PARAMS>(mergedParams, template.requestValidation);\n } catch (e) {\n throw buildValidationError(e as ValidationError, serviceName);\n }\n }\n const apiRequest = template.buildRequest(mergedParams);\n const headers = generateTomTomHeaders(mergedParams);\n params.onAPIRequest?.(apiRequest);\n\n try {\n const apiResponse = await template.sendRequest(apiRequest, headers, { signal: params.signal });\n params.onAPIResponse?.(apiRequest, apiResponse);\n return template.parseResponse(await apiResponse.data, mergedParams);\n } catch (e) {\n params.onAPIResponse?.(apiRequest, e);\n throw buildResponseError(e, serviceName, template.parseResponseError, params.signal);\n }\n};\n","import { isProxyCredentialsMode, RetryConfig, TomTomConfig, type TomTomHeaders } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, GetObject, ParsedFetchResponse, PostObject, RequestOptions } from './types/fetch';\n\n/**\n * Custom error class for HTTP fetch errors.\n */\nclass FetchError extends Error {\n public readonly status: number;\n public readonly data?: unknown;\n\n constructor(status: number, message?: string, data?: unknown) {\n super(message ?? `HTTP Error ${status}`);\n this.name = 'FetchError';\n this.status = status;\n this.data = data;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FetchError);\n }\n }\n}\n\n// Returns the response as a JSON object or throws an error if the response isn't successful.\nconst returnOrThrow = async <T>(response: Response): ParsedFetchResponse<T> => {\n if (response.ok) {\n return { data: await response.json(), status: response.status };\n }\n let message: string | undefined;\n let errorBody;\n const contentType = response.headers.get('content-type');\n if (response.bodyUsed) {\n message = response.statusText;\n } else if (contentType?.includes('application/json')) {\n errorBody = await response.json();\n message = errorBody?.errorText ?? errorBody?.message ?? errorBody?.detailedError?.message;\n } else if (contentType?.includes('text/xml')) {\n errorBody = await response.text();\n message = response.statusText;\n }\n\n throw new FetchError(response.status, message, errorBody);\n};\n\n// Waits for the given duration, rejecting with the abort reason if the signal fires.\nconst sleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise((resolve, reject) => {\n // addEventListener never fires for an abort that already happened\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\n/**\n * Wraps a fetch call with automatic retry on 429 (Too Many Requests) responses.\n * Respects the Retry-After header when present, otherwise uses exponential backoff.\n * A given signal cancels both the in-flight request and any pending backoff wait.\n * @ignore\n */\nconst fetchWithRetry = async (fetchFn: () => Promise<Response>, signal?: AbortSignal): Promise<Response> => {\n const retryConfig = TomTomConfig.instance.get().retry;\n if (!retryConfig) {\n return fetchFn();\n }\n\n const { initialWaitMs, backoffFactor, timeoutMs } = retryConfig as Required<RetryConfig>;\n\n const startTime = Date.now();\n let backoffMs = initialWaitMs;\n let response = await fetchFn();\n\n while (response.status === 429) {\n const retryAfterSeconds = Number.parseInt(response.headers.get('retry-after') ?? '', 10);\n const waitMs = Number.isFinite(retryAfterSeconds) ? retryAfterSeconds * 1000 : backoffMs;\n\n if (Date.now() - startTime + waitMs > timeoutMs) {\n break;\n }\n\n // Drain the body to free the connection\n await response.text().catch(() => {});\n\n await sleep(waitMs, signal);\n backoffMs *= backoffFactor;\n response = await fetchFn();\n }\n\n return response;\n};\n\nconst proxyModeCredentials = (): RequestCredentials | undefined => (isProxyCredentialsMode() ? 'include' : undefined);\n\n/**\n * Fetches the given HTTP JSON resource with an HTTP GET request and returns a promise with the response as a JSON object.\n * If the response isn't successful, it returns a rejected promise with the http error code.\n * @ignore\n * @param input A URL or a GET object with URL and optional service-specific headers.\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n * @param options Per-call HTTP options, such as the `AbortSignal` used to cancel the request.\n */\nexport const get = async <T>(\n input: URL | GetObject,\n sdkHeaders: TomTomHeaders,\n options?: RequestOptions,\n): ParsedFetchResponse<T> =>\n returnOrThrow(\n await fetchWithRetry(() => {\n const credentials = proxyModeCredentials();\n const { url, headers: inputHeaders } = input instanceof URL ? { url: input, headers: undefined } : input;\n const init: RequestInit = { headers: { ...sdkHeaders, ...inputHeaders } };\n if (credentials) init.credentials = credentials;\n if (options?.signal) init.signal = options.signal;\n\n return fetch(url, init);\n }, options?.signal),\n );\n\n/**\n * Fetches the given HTTP JSON resource with an HTTP POST request and returns a promise with the response as a JSON object.\n * If the response isn't successful, it returns a rejected promise with the http error code.\n * @ignore\n * @param input The POST object with URL, optional payload, and optional service-specific headers.\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n * @param options Per-call HTTP options, such as the `AbortSignal` used to cancel the request.\n */\nexport const post = async <T, D>(\n input: PostObject<D>,\n sdkHeaders: TomTomHeaders,\n options?: RequestOptions,\n): ParsedFetchResponse<T> =>\n returnOrThrow(\n await fetchWithRetry(() => {\n const credentials = proxyModeCredentials();\n const init: RequestInit = {\n method: 'POST',\n body: JSON.stringify(input.data),\n headers: { ...sdkHeaders, ...input.headers, 'Content-Type': 'application/json' },\n };\n if (credentials) init.credentials = credentials;\n if (options?.signal) init.signal = options.signal;\n\n return fetch(input.url, init);\n }, options?.signal),\n );\n\n/**\n * Fetches the given HTTP JSON resource with the given HTTP operation and URL/Payload as applicable.\n * * Useful for services which can use different HTTP methods depending on the parameters.\n * @param input The input object (e.g. containing either GET or POST data)\n * @param sdkHeaders SDK-wide TomTom headers to be sent with the request.\n * @param options Per-call HTTP options, such as the `AbortSignal` used to cancel the request.\n * @ignore\n */\nexport const fetchWith = async <T, D = void>(\n input: FetchInput<D>,\n sdkHeaders: TomTomHeaders,\n options?: RequestOptions,\n): ParsedFetchResponse<T> => {\n if (input.method === 'GET') return get<T>(input, sdkHeaders, options);\n if (input.method === 'POST') return post<T, D>(input, sdkHeaders, options);\n throw new Error(`Unsupported HTTP method received: ${(input as { method: string }).method}`);\n};\n","import { z } from 'zod';\n\n/**\n * @ignore\n */\nexport const lineStringCoordsSchema = z.array(z.array(z.number()));\n\n/**\n * @ignore\n */\nexport const geometrySchema = z\n .object({\n type: z.enum([\n 'Point',\n 'MultiPoint',\n 'LineString',\n 'MultiLineString',\n 'Polygon',\n 'MultiPolygon',\n 'GeometryCollection',\n 'Circle',\n ]),\n coordinates: z.union([\n z.array(z.number()),\n lineStringCoordsSchema,\n z.array(z.array(z.array(z.number()))),\n z.array(z.array(z.array(z.array(z.number())))),\n ]),\n radius: z.optional(z.number()),\n radiusMeters: z.optional(z.number()),\n bbox: z.optional(z.array(z.number())),\n })\n .check(\n z.refine(\n (data) => (data.type === 'Circle' ? Boolean(data.radius) : true),\n 'type: \"Circle\" must have radius property',\n ),\n );\n\n/**\n * @ignore\n */\nexport const featureSchema = z.object({\n type: z.literal('Feature'),\n geometry: geometrySchema,\n id: z.optional(z.union([z.string(), z.number()])),\n properties: z.unknown().optional(),\n bbox: z.optional(z.array(z.number())),\n});\n\n/**\n * @ignore\n */\nexport const featureCollectionSchema = z.object({\n type: z.literal('FeatureCollection'),\n features: z.array(featureSchema),\n id: z.optional(z.union([z.string(), z.number()])),\n properties: z.unknown().optional(),\n bbox: z.optional(z.array(z.number())),\n});\n\n/**\n * @ignore\n */\nexport const hasLngLatSchema = z.union([\n z.tuple([z.number().min(-180).max(180), z.number().min(-90).max(90)]),\n z.tuple([z.number().min(-180).max(180), z.number().min(-90).max(90), z.number()]),\n z.object({\n type: z.literal('Point'),\n coordinates: z.array(z.number()),\n }),\n featureSchema,\n]);\n\n/**\n * @ignore\n */\nconst geoJsonbBoxSchema = z.union([\n z.array(z.number()).refine((arr) => arr.length === 4, { message: 'BBox must have 4 elements' }),\n z.array(z.number()).refine((arr) => arr.length === 6, { message: 'BBox must have 6 elements' }),\n]);\n\n/**\n * @ignore\n */\nexport const GeoJSONSchema = z.union([geometrySchema, featureSchema, featureCollectionSchema]);\n\n/**\n * @ignore\n */\nexport const hasBBoxSchema = z.union([geoJsonbBoxSchema, GeoJSONSchema, z.array(GeoJSONSchema)]);\n","import { views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { hasLngLatSchema } from './geometriesSchema';\n\n/**\n * @ignore\n */\nexport const commonPlacesParamsSchema = z.object({\n query: z.string().optional(),\n position: hasLngLatSchema.optional(),\n limit: z.number().max(100).optional(),\n extendedPostalCodesFor: z.array(z.string()).optional(),\n mapcodes: z.array(z.string()).optional(),\n view: z.enum(views).optional(),\n});\n","import { connectorTypes, geographyTypes, poiCategories } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\n\n/** The country half of `filters`, which geometry and along-route search do not take. @ignore */\nexport const geographicFiltersSchema = z.object({\n countries: z.array(z.string()).optional(),\n});\n\n/** The POI-attribute half of `filters`. @ignore */\nexport const placeFiltersSchema = z.object({\n poiCategories: z.array(z.enum(poiCategories)).optional(),\n poiBrands: z.array(z.string()).optional(),\n indexes: z.array(z.string()).optional(),\n geographyTypes: z.array(z.enum(geographyTypes)).optional(),\n connectors: z.array(z.enum(connectorTypes)).optional(),\n fuelTypes: z.array(z.string()).optional(),\n minPowerKW: z.number().optional(),\n maxPowerKW: z.number().optional(),\n});\n\n/** Both halves in one object, for fuzzy search. @ignore */\nexport const searchFiltersSchema = geographicFiltersSchema.extend(placeFiltersSchema.shape);\n","import { z } from 'zod';\nimport { commonPlacesParamsSchema } from '../shared/schema/commonPlacesParamsSchema';\nimport { placeFiltersSchema } from '../shared/schema/searchFiltersSchema';\n\nconst searchExtraParamsOptional = z.object({\n filters: placeFiltersSchema.optional(),\n openingHours: z.string().optional(),\n timeZone: z.string().optional(),\n relatedPois: z.string().optional(),\n minFuzzyLevel: z.number().optional(),\n maxFuzzyLevel: z.number().optional(),\n});\n\n/**\n * @ignore\n */\nexport const commonSearchParamsSchema = commonPlacesParamsSchema.extend(searchExtraParamsOptional.shape);\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { lineStringCoordsSchema } from '../shared/schema/geometriesSchema';\n\nconst lineStringSchema = z.object({\n type: z.literal('LineString'),\n coordinates: lineStringCoordsSchema,\n});\n\nconst routeFeatureSchema = z.object({\n type: z.literal('Feature'),\n geometry: lineStringSchema,\n});\n\nconst positionsArraySchema = lineStringCoordsSchema;\n\nconst routeSchema = z.union([lineStringSchema, routeFeatureSchema, positionsArraySchema]);\n\nconst alongRouteSearchRequestMandatory = z.object({\n route: routeSchema,\n maxDetourTimeSeconds: z.number().int().positive(),\n});\n\nconst alongRouteSearchRequestOptional = z.object({\n sortBy: z.enum(['detourTime', 'detourOffset']).optional(),\n});\n\n/**\n * @ignore\n */\nexport const alongRouteSearchRequestSchema = commonSearchParamsSchema.extend({\n ...alongRouteSearchRequestMandatory.shape,\n ...alongRouteSearchRequestOptional.shape,\n});\n","/**\n * @ignore\n * @param input\n */\nexport const arrayToCSV = (input: unknown | unknown[]): string =>\n !input ? '' : Array.isArray(input) ? input.join(',') : typeof input === 'string' ? input : String(input);\n\n/**\n * Samples, if necessary, the given array to fit within the given max length.\n * * If the array already fits within maxLength, the same array is returned.\n * * The sampling is done by spreading the array points at a constant increment.\n * * The first and last points are always included if array and max lengths are > 1.\n * * The sampling is done with speed in mind, so it's not guaranteed to always fit maxLength. Very often it will be shorter.\n *\n * Use this when needing to quickly simplify an array to fit some max length without caring too much on the lost detail.\n * * If both the array and max length are long enough, the loss of detail won't likely be an issue.\n * @ignore\n */\nexport const sampleWithinMaxLength = <T>(array: T[], maxLength: number): T[] => {\n const length = array.length;\n if (length <= maxLength) {\n return array;\n }\n\n const sampledArray = [];\n let i;\n const increment = Math.ceil(length / maxLength);\n for (i = 0; i < length; i += increment) {\n sampledArray.push(array[i]);\n }\n // ensuring the last point is always added:\n if (maxLength > 1 && i >= length - increment) {\n if (sampledArray.length < maxLength) {\n sampledArray.push(array[length - 1]);\n } else {\n sampledArray[sampledArray.length - 1] = array[length - 1];\n }\n }\n\n return sampledArray;\n};\n","import type { HasLngLat, POICategory, TomTomAPIHeaders } from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON, getPosition, poiCategoriesToID } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport { arrayToCSV } from '../arrays';\nimport type { CommonServiceParams } from '../serviceTypes';\nimport type { GeoBias } from '../types/geoBias';\n\n/**\n * {@link CommonServiceParams} as a request builder receives them: plus the API version `callService`\n * takes from the service's template, never from the caller.\n *\n * @ignore\n */\nexport type InternalServiceParams = CommonServiceParams & { apiVersion?: number };\n\n/**\n * The credential pair: the API key and the API version.\n *\n * @remarks\n * The `apiKey` guard is what makes proxy deployments work: with no key configured nothing is\n * emitted, and `fetch` attaches `credentials: 'include'` so the proxy can inject the real one.\n */\nconst buildCredentialHeaders = (params: InternalServiceParams): TomTomAPIHeaders => ({\n ...(params.apiKey && { 'TomTom-Api-Key': params.apiKey }),\n ...(!isNil(params.apiVersion) && { 'TomTom-Api-Version': String(params.apiVersion) }),\n});\n\n/**\n * Builds the TomTom API request headers common to every service: the credential pair plus the\n * language. Service request builders spread the result and add any service-specific headers\n * (e.g. `Attributes`) on top.\n * @ignore\n */\nexport const buildCommonServiceRequestHeaders = (params: InternalServiceParams): TomTomAPIHeaders => ({\n ...buildCredentialHeaders(params),\n ...(params.language && { 'Accept-Language': params.language }),\n});\n\n/**\n * @ignore\n * @param urlParams\n * @param params\n */\nexport const appendCommonParams = (urlParams: URLSearchParams, params: InternalServiceParams): void => {\n urlParams.append('apiVersion', String(params.apiVersion));\n if (params.apiKey) {\n urlParams.append('key', params.apiKey);\n }\n params.language && urlParams.append('language', params.language);\n};\n\n/**\n * The credential headers for a places request: the API key and the API version, and nothing else.\n *\n * @remarks\n * Deliberately **not** {@link buildCommonServiceRequestHeaders}, which also emits\n * `Accept-Language`. The `apiVersion=1` places endpoints ignore that header — a request carrying it\n * comes back in the default language, with no error. Only `language=` in the query string\n * localizes, so {@link appendPlacesLanguageParam} keeps it there. Probed against production; the\n * API documentation does not state it.\n *\n * `reverseGeocode` is the exception and uses the common builder, because it is pinned to\n * `apiVersion: 2`, where the header is honoured.\n *\n * @ignore\n */\nexport const buildPlacesRequestHeaders = (params: InternalServiceParams): TomTomAPIHeaders =>\n buildCredentialHeaders(params);\n\n/**\n * Appends the query parameters a places request still carries in the URL.\n *\n * @remarks\n * Only `language`. The key and the version moved to headers — see\n * {@link buildPlacesRequestHeaders} for why this one could not follow them.\n *\n * @ignore\n */\nexport const appendPlacesLanguageParam = (urlParams: URLSearchParams, params: CommonServiceParams): void => {\n params.language && urlParams.append('language', params.language);\n};\n\n/**\n * Asks for EV charging places as a station → charging point → connector hierarchy, the model `ev/id`\n * returns, instead of a flat connector list. Undocumented; honoured by search, geometry search,\n * along-route search and `place.json`.\n *\n * @ignore\n */\nexport const appendEVModelVersionParam = (urlParams: URLSearchParams): void => {\n urlParams.append('evModelVersion', '3');\n};\n\n/**\n * Adds parameter from the array by repeating each array part into a query parameter of the same name.\n * E.g. ...&avoid=motorways&avoid=ferries&...\n * @ignore\n * @param urlParams\n * @param paramName\n * @param paramArray\n */\nexport const appendByRepeatingParamName = (\n urlParams: URLSearchParams,\n paramName: string,\n paramArray?: string[],\n): void => {\n for (const param of paramArray || []) {\n urlParams.append(paramName, param);\n }\n};\n\n/**\n * @ignore\n */\nexport const appendByJoiningParamValue = (\n urlParams: URLSearchParams,\n name: string,\n values?: string[] | number[] | (string | number)[],\n): void => {\n if (Array.isArray(values) && values.length > 0) {\n urlParams.append(name, values.join(','));\n }\n};\n\n/**\n * @ignore\n */\nexport const appendOptionalParam = (\n urlParams: URLSearchParams,\n name: string,\n value?: string | number | boolean,\n): void => {\n !isNil(value) && urlParams.append(name, String(value));\n};\n\n/**\n * Adds lat and lon parameters to the url.\n * @ignore\n * @param urlParams\n * @param hasLngLat\n */\nexport const appendLatLonParamsFromPosition = (urlParams: URLSearchParams, hasLngLat: HasLngLat | undefined): void => {\n const position = getPosition(hasLngLat);\n if (position) {\n urlParams.append('lat', String(position[1]));\n urlParams.append('lon', String(position[0]));\n }\n};\n\n/**\n * Adds whichever geographic bias was given: `lat`/`lon` plus `radius` for a point, or the\n * `topLeft`/`btmRight` corner pair for a bounding box.\n *\n * @ignore\n */\nexport const appendGeoBiasParams = (urlParams: URLSearchParams, geoBias: GeoBias | undefined): void => {\n if (!geoBias) return;\n\n if (geoBias.position !== undefined) {\n appendLatLonParamsFromPosition(urlParams, geoBias.position);\n appendOptionalParam(urlParams, 'radius', geoBias.radiusMeters);\n\n return;\n }\n\n const bbox = bboxFromGeoJSON(geoBias.boundingBox);\n if (!bbox) return;\n\n urlParams.append('topLeft', arrayToCSV([bbox[3], bbox[0]]));\n urlParams.append('btmRight', arrayToCSV([bbox[1], bbox[2]]));\n};\n\n/**\n * Map POICategory values to their numeric IDs for use in API requests.\n * @ignore\n */\nexport const mapPOICategoriesToIDs = (categories: POICategory[]): number[] =>\n categories.map((category) => poiCategoriesToID[category]);\n","import type { AlongRouteSearchParams } from '../../along-route-search';\nimport type { FuzzySearchParams } from '../../fuzzy-search';\nimport type { GeometrySearchParams } from '../../geometry-search';\nimport {\n appendByJoiningParamValue,\n appendEVModelVersionParam,\n appendOptionalParam,\n appendPlacesLanguageParam,\n mapPOICategoriesToIDs,\n} from './requestBuildingUtils';\n\n/**\n * Appends request parameters common to search APIs such as fuzzy + geometry search.\n * * Mutates the given searchURL with the appended parameters.\n * @param searchUrl The search URL to append parameters to. Should come without any parameters at this point.\n * @param params The search parameters, with global configuration already merged into them.\n */\nexport const appendCommonSearchParams = (\n searchUrl: URL,\n params: FuzzySearchParams | GeometrySearchParams | AlongRouteSearchParams,\n): void => {\n const urlParams = searchUrl.searchParams;\n const { filters } = params;\n appendPlacesLanguageParam(urlParams, params);\n appendOptionalParam(urlParams, 'limit', params.limit);\n appendByJoiningParamValue(urlParams, 'fuelSet', filters?.fuelTypes);\n appendByJoiningParamValue(urlParams, 'idxSet', filters?.indexes);\n appendByJoiningParamValue(urlParams, 'brandSet', filters?.poiBrands);\n filters?.poiCategories &&\n appendByJoiningParamValue(urlParams, 'categorySet', mapPOICategoriesToIDs(filters.poiCategories));\n appendByJoiningParamValue(urlParams, 'connectorSet', filters?.connectors);\n appendByJoiningParamValue(urlParams, 'mapcodes', params.mapcodes);\n appendByJoiningParamValue(urlParams, 'extendedPostalCodesFor', params.extendedPostalCodesFor);\n\n appendOptionalParam(urlParams, 'minPowerKW', filters?.minPowerKW);\n appendOptionalParam(urlParams, 'maxPowerKW', filters?.maxPowerKW);\n appendOptionalParam(urlParams, 'view', params.view);\n appendOptionalParam(urlParams, 'openingHours', params.openingHours);\n appendOptionalParam(urlParams, 'timeZone', params.timeZone);\n appendOptionalParam(urlParams, 'relatedPois', params.relatedPois);\n appendByJoiningParamValue(urlParams, 'entityTypeSet', filters?.geographyTypes);\n appendEVModelVersionParam(urlParams);\n};\n","import type { CommonServiceParams } from '../serviceTypes';\n\n/**\n * The path every places endpoint hangs off.\n *\n * @ignore\n */\nexport const PLACES_URL_PATH = '/maps/orbis/places';\n\n/**\n * The URL for one places endpoint.\n *\n * @remarks\n * `endpoint` is the path below {@link PLACES_URL_PATH}, already interpolated where it carries a\n * query — `'search/pizza.json'`, `'poiCategories.json'`, `'ev/id'`.\n *\n * @ignore\n */\nexport const resolvePlacesEndpointUrl = (params: CommonServiceParams, endpoint: string): string =>\n `${params.commonBaseURL}${PLACES_URL_PATH}/${endpoint}`;\n","import type { LineString } from 'geojson';\nimport { appendCommonSearchParams } from '../shared/request/commonSearchRequestBuilder';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { appendOptionalParam, buildPlacesRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { AlongRouteSearchParams, AlongRouteSearchRequestAPI } from './types';\n\nconst toLineString = (route: AlongRouteSearchParams['route']): LineString => {\n if (Array.isArray(route)) {\n return { type: 'LineString', coordinates: route };\n }\n return route.type === 'Feature' ? route.geometry : route;\n};\n\nconst buildUrlBasePath = (params: AlongRouteSearchParams): string =>\n resolvePlacesEndpointUrl(params, `searchAlongRoute/${encodeURIComponent(params.query ?? '')}.json`);\n\n/**\n * Default function for building an along-route search request from {@link AlongRouteSearchParams}.\n * @param params The along-route search parameters, with global configuration already merged into them.\n */\nexport const buildAlongRouteSearchRequest = (params: AlongRouteSearchParams): AlongRouteSearchRequestAPI => {\n const url = new URL(buildUrlBasePath(params));\n appendCommonSearchParams(url, params);\n const urlParams = url.searchParams;\n urlParams.append('maxDetourTime', String(params.maxDetourTimeSeconds));\n appendOptionalParam(urlParams, 'sortBy', params.sortBy);\n appendOptionalParam(urlParams, 'spreadingMode', 'plan');\n\n return {\n url,\n headers: buildPlacesRequestHeaders(params),\n data: {\n route: {\n points: toLineString(params.route).coordinates.map((coord) => ({\n lat: coord[1],\n lon: coord[0],\n })),\n },\n },\n };\n};\n","import type {\n ChargingPointAvailability,\n ChargingPointStatus,\n ChargingStation,\n Connector,\n ConnectorAvailability,\n ConnectorCount,\n StaticChargingPoint,\n} from '@tomtom-org/maps-sdk/core';\n\n/**\n * @ignore\n */\nexport const toChargingPointAvailability = (chargingStations: ChargingStation[]): ChargingPointAvailability => {\n const availability: ChargingPointAvailability = { count: 0, statusCounts: {} };\n for (const station of chargingStations) {\n for (const chargingPoint of station.chargingPoints) {\n availability.count++;\n availability.statusCounts[chargingPoint.status] =\n (availability.statusCounts[chargingPoint.status] || 0) + 1;\n }\n }\n return availability;\n};\n\n// Two connectors can be considered equal when they have the same type and power:\nconst areEqual = (connectorA: Connector, connectorB: Connector) =>\n connectorA.type === connectorB.type && connectorA.ratedPowerKW === connectorB.ratedPowerKW;\n\nconst addConnectorCount = (\n connectors: Connector[] | undefined,\n counts: ConnectorCount[], // we are mutating this input\n): void => {\n if (!connectors) {\n // defensive check, sometimes connectors are undefined\n return;\n }\n for (const connector of connectors) {\n const existingCount = counts.find((connectorCount) => areEqual(connector, connectorCount.connector));\n if (existingCount) {\n existingCount.count++;\n } else {\n // new count entry:\n counts.push({ connector, count: 1 });\n }\n }\n};\n\nconst addConnectorCountAndStatus = (\n connectors: Connector[] | undefined,\n status: ChargingPointStatus | undefined,\n availabilities: ConnectorAvailability[], // we are mutating this input\n): void => {\n if (!connectors) {\n // defensive check, sometimes connectors are undefined\n return;\n }\n for (const connector of connectors) {\n const existingAvailability = availabilities.find((connectorAvailability) =>\n areEqual(connector, connectorAvailability.connector),\n );\n if (existingAvailability) {\n existingAvailability.count++;\n if (status) {\n // we're mutating the input object here:\n const statusCounts = existingAvailability.statusCounts;\n existingAvailability.statusCounts[status] = (statusCounts[status] || 0) + 1;\n }\n } else {\n // new availability entry:\n availabilities.push({ connector, count: 1, statusCounts: status ? { [status]: 1 } : {} });\n }\n }\n};\n\n/**\n * @ignore\n */\nexport const toConnectorCounts = (chargingStations: ChargingStation<StaticChargingPoint>[]): ConnectorCount[] => {\n const counts: ConnectorCount[] = [];\n for (const station of chargingStations) {\n for (const chargingPoint of station.chargingPoints) {\n addConnectorCount(chargingPoint.connectors, counts);\n }\n }\n return counts;\n};\n\n/**\n * @ignore\n */\nexport const toConnectorBasedAvailabilities = (chargingStations: ChargingStation[]): ConnectorAvailability[] => {\n const availabilities: ConnectorAvailability[] = [];\n for (const station of chargingStations) {\n for (const chargingPoint of station.chargingPoints) {\n addConnectorCountAndStatus(chargingPoint.connectors, chargingPoint.status, availabilities);\n }\n }\n return availabilities;\n};\n","import type { ChargingSpeed, ChargingStation, StaticChargingPoint } from '@tomtom-org/maps-sdk/core';\nimport type { ChargingStationAPI } from './types/apiPlacesResponseTypes';\n\n/**\n * @ignore\n */\nexport const toChargingSpeed = (powerInKW: number): ChargingSpeed => {\n if (powerInKW < 12) {\n return 'slow';\n } else if (powerInKW < 50) {\n return 'regular';\n } else if (powerInKW < 150) {\n return 'fast';\n }\n return 'ultra-fast';\n};\n\n/**\n * Adds each connector's charging speed to API charging stations, keeping the rest of each charging point.\n * @ignore\n */\nexport const toChargingStations = <Status extends object>(\n stations: ChargingStationAPI<Status>[],\n): ChargingStation<StaticChargingPoint & Status>[] =>\n stations.map((station) => ({\n ...station,\n chargingPoints: station.chargingPoints.map((chargingPoint) => ({\n ...chargingPoint,\n capabilities: chargingPoint.capabilities ?? [],\n restrictions: chargingPoint.restrictions ?? [],\n connectors: chargingPoint.connectors?.map((connector) => ({\n ...connector,\n chargingSpeed: toChargingSpeed(connector.ratedPowerKW),\n })),\n })),\n }));\n","import type { BBox } from '@tomtom-org/maps-sdk/core';\nimport type { Position } from 'geojson';\nimport type { BoundingBoxAPI, BoundingBoxTopLeftAPI, LatLonAPI } from './types/apiPlacesResponseTypes';\n\n/**\n * @ignore\n * @param csv\n */\nexport const csvLatLngToPosition = (csv: string): Position => {\n const splitLatLng = csv.split(',');\n return [Number(splitLatLng[1]), Number(splitLatLng[0])];\n};\n\n/**\n * @ignore\n * @param position\n */\nexport const positionToCSVLatLon = (position: Position): string => `${position[1]},${position[0]}`;\n\nconst hasTopLeftPoint = (bbox: BoundingBoxAPI): bbox is BoundingBoxTopLeftAPI => {\n return (<BoundingBoxTopLeftAPI>bbox).topLeftPoint !== undefined;\n};\n\n/**\n * @ignore\n * @param apiBBox\n */\nexport const apiToGeoJSONBBox = (apiBBox: BoundingBoxAPI): BBox => {\n let westSouth: Position;\n let eastNorth: Position;\n if (hasTopLeftPoint(apiBBox)) {\n westSouth = [apiBBox.topLeftPoint.lon, apiBBox.btmRightPoint.lat];\n eastNorth = [apiBBox.btmRightPoint.lon, apiBBox.topLeftPoint.lat];\n } else {\n westSouth = csvLatLngToPosition(apiBBox.southWest);\n eastNorth = csvLatLngToPosition(apiBBox.northEast);\n }\n return [westSouth[0], westSouth[1], eastNorth[0], eastNorth[1]];\n};\n\n/**\n * @ignore\n * @param point\n */\nexport const latLonAPIToPosition = (point: LatLonAPI): Position => {\n return [point.lon, point.lat];\n};\n","import { z } from 'zod';\nimport { SDKError } from './errors';\nimport type { SummaryAPI } from './types/apiPlacesResponseTypes';\nimport type { ServiceName } from './types/servicesTypes';\n\n/** The highest `ofs` the places backend serves; it clamps a later one to this, re-serving the same page. */\nconst MAX_OFFSET = 1900;\n\n/** What a cursor carries: the `ofs` the next page starts at. Internal, and free to change. */\nconst searchCursorStateSchema = z.object({ ofs: z.number().int().nonnegative() });\n\ntype SearchCursorState = z.infer<typeof searchCursorStateSchema>;\n\nconst CURSOR_ERROR_MESSAGE =\n 'The `cursor` is not one this SDK can read. Pass back the `nextCursor` from a previous response ' +\n 'rather than constructing one.';\n\n// Plain `btoa`/`atob` suffice while the state is ASCII JSON; a state carrying text would need a UTF-8 step.\nconst parseCursor = (cursor: string): unknown => {\n try {\n return JSON.parse(atob(cursor));\n } catch {\n return undefined;\n }\n};\n\nconst readCursorState = (cursor: string): SearchCursorState | undefined =>\n searchCursorStateSchema.safeParse(parseCursor(cursor)).data;\n\n/**\n * Validates a request's `cursor`: it must read back as a cursor this SDK produced.\n * @ignore\n */\nexport const searchCursorSchema = z.string().refine((cursor) => !!readCursorState(cursor), CURSOR_ERROR_MESSAGE);\n\n/**\n * Mints the opaque cursor for the page after `summary`, or `undefined` when there is none.\n *\n * @remarks\n * The cursor wraps the backend's `ofs`, so the pagination mechanism can change without a caller\n * noticing. It is stateless — everything needed to continue lives in the string — and its base64\n * is a closed door rather than a lock: nothing documents the format and nothing should parse it.\n */\nexport const encodeSearchCursor = ({\n offset,\n numResults,\n totalResults,\n}: Pick<SummaryAPI, 'offset' | 'numResults' | 'totalResults'>): string | undefined => {\n // An empty page must end pagination: its successor would start at the same `ofs`, and a\n // `while (nextCursor)` loop would never end.\n if (numResults === 0) return undefined;\n\n // The next page starts after this page's `numResults`, not after the `limit` asked for.\n const nextOffset = offset + numResults;\n if (nextOffset >= totalResults || nextOffset > MAX_OFFSET) return undefined;\n\n const state: SearchCursorState = { ofs: nextOffset };\n return btoa(JSON.stringify(state));\n};\n\n/**\n * Reads a cursor back into the offset it stands for.\n *\n * @remarks\n * Only the format is checked: request validation already rejects a foreign value through\n * {@link searchCursorSchema}, so this throw is for a caller who turned validation off.\n *\n * @throws SDKError when the value does not read back as a cursor, the empty string included.\n */\nexport const decodeSearchCursor = (cursor: string, serviceName: ServiceName): number => {\n const state = readCursorState(cursor);\n if (!state) throw new SDKError(CURSOR_ERROR_MESSAGE, serviceName);\n\n return state.ofs;\n};\n\n/**\n * Appends the `ofs` a request's `cursor` stands for. No cursor means the first page.\n *\n * @throws SDKError when the value does not read back as a cursor, the empty string included.\n */\nexport const appendSearchCursorParam = (\n urlParams: URLSearchParams,\n cursor: string | undefined,\n serviceName: ServiceName,\n): void => {\n if (cursor === undefined) return;\n\n urlParams.append('ofs', String(decodeSearchCursor(cursor, serviceName)));\n};\n","import type { Moment, OpeningHours, Place, POICategory, SearchPlaceProps, TimeRange } from '@tomtom-org/maps-sdk/core';\nimport { poiIDsToCategories, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { omit } from 'lodash-es';\nimport { toConnectorCounts } from '../ev-charging-stations-availability/connectorAvailability';\nimport { toChargingStations } from './ev';\nimport { apiToGeoJSONBBox, latLonAPIToPosition } from './geometry';\nimport { encodeSearchCursor } from './searchCursor';\nimport type {\n BrandAPI,\n CategoryAPI,\n CommonSearchPlaceResultAPI,\n MomentAPI,\n OpeningHoursAPI,\n SummaryAPI,\n} from './types/apiPlacesResponseTypes';\nimport type { SearchSummary } from './types/searchSummary';\n\nconst parseYyyymmddDate = (dateYyyymmdd: string): { year: number; month: number; day: number } => {\n const splitDate = dateYyyymmdd.split('-');\n return {\n year: Number.parseInt(splitDate[0]),\n month: Number.parseInt(splitDate[1]),\n day: Number.parseInt(splitDate[2]),\n };\n};\n\nconst parseMoment = (momentApi: MomentAPI): Moment => {\n const { year, month, day } = parseYyyymmddDate(momentApi.date);\n return {\n dateYYYYMMDD: momentApi.date,\n year,\n month,\n day,\n hour: momentApi.hour,\n minute: momentApi.minute,\n date: new Date(year, month - 1, day, momentApi.hour, momentApi.minute),\n };\n};\n\nconst alwaysOpenInThisPeriod = (timeRanges: TimeRange[]): boolean =>\n timeRanges.length === 1 && timeRanges[0].start.hour === 0 && timeRanges[0].end.hour === 0;\n\n/**\n * @ignore\n */\nexport const parseOpeningHours = (openingHoursApi: OpeningHoursAPI): OpeningHours => {\n const timeRanges = openingHoursApi.timeRanges.map(\n (timeRangeApi): TimeRange => ({\n start: parseMoment(timeRangeApi.startTime),\n end: parseMoment(timeRangeApi.endTime),\n }),\n );\n return {\n mode: openingHoursApi.mode,\n timeRanges,\n alwaysOpenThisPeriod: alwaysOpenInThisPeriod(timeRanges),\n };\n};\n\n/**\n * Shared response parsing between geometry search and place by id service.\n * @ignore\n */\nexport const parseSearchAPIResult = (result: CommonSearchPlaceResultAPI): Place<SearchPlaceProps> => {\n const {\n position,\n entryPoints,\n poi,\n id,\n dist,\n boundingBox,\n chargingStations: chargingStationsAPI,\n ...rest\n } = result;\n const chargingStations = chargingStationsAPI && toChargingStations(chargingStationsAPI);\n return {\n type: 'Feature',\n geometry: toPointGeometry(latLonAPIToPosition(position)),\n ...(boundingBox && { bbox: apiToGeoJSONBBox(boundingBox) }),\n id,\n properties: {\n ...omit(rest, 'viewport'),\n ...(dist && { distance: dist }),\n ...(entryPoints?.length && {\n entryPoints: entryPoints.map((entrypoint) => ({\n ...entrypoint,\n position: latLonAPIToPosition(entrypoint.position),\n })),\n }),\n ...(chargingStations?.length && {\n chargingPark: { connectors: toConnectorCounts(chargingStations), chargingStations },\n }),\n ...(poi && {\n poi: {\n name: poi.name,\n categories: (\n poi.categorySet?.map((c: CategoryAPI): POICategory => poiIDsToCategories[c.id]) ?? []\n ).filter((c: POICategory) => !!c),\n localizedCategories: poi.categories ?? [],\n ...(poi.phone && { phone: poi.phone }),\n ...(poi.brands && { brands: poi.brands.map((brand: BrandAPI) => brand.name) }),\n ...(poi.url && { url: poi.url }),\n ...(poi.openingHours && { openingHours: parseOpeningHours(poi.openingHours) }),\n ...(poi.timeZone && { timeZone: poi.timeZone }),\n },\n }),\n },\n };\n};\n\n/**\n * @ignore\n */\nexport const parseSummaryAPI = (summary: SummaryAPI, options?: { paginated: boolean }): SearchSummary => {\n // Only a service that takes a `cursor` may hand one out, or `while (nextCursor)` refetches the\n // same page forever.\n const nextCursor = options?.paginated ? encodeSearchCursor(summary) : undefined;\n\n return {\n ...(summary.geoBias && { geoBias: latLonAPIToPosition(summary.geoBias) }),\n ...(nextCursor && { nextCursor }),\n ...omit(summary, ['geoBias', 'offset']),\n };\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { AlongRouteSearchResponse, AlongRouteSearchResponseAPI } from './types';\n\n/**\n * Default function to parse an along-route search response.\n * @param apiResponse The API response.\n */\nexport const parseAlongRouteSearchResponse = (apiResponse: AlongRouteSearchResponseAPI): AlongRouteSearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { post } from '../shared/fetch';\nimport { alongRouteSearchRequestSchema } from './alongRouteSearchRequestSchema';\nimport { buildAlongRouteSearchRequest } from './requestBuilder';\nimport { parseAlongRouteSearchResponse } from './responseParser';\nimport type {\n AlongRouteSearchParams,\n AlongRouteSearchRequestAPI,\n AlongRouteSearchResponse,\n AlongRouteSearchResponseAPI,\n} from './types';\n\n/**\n * Along-route search service template type.\n * @ignore\n */\nexport type AlongRouteSearchTemplate = ServiceTemplate<\n AlongRouteSearchParams,\n AlongRouteSearchRequestAPI,\n AlongRouteSearchResponseAPI,\n AlongRouteSearchResponse\n>;\n\n/**\n * Along-route search service template main implementation.\n * @ignore\n */\nexport const alongRouteSearchTemplate: AlongRouteSearchTemplate = {\n requestValidation: { schema: alongRouteSearchRequestSchema },\n buildRequest: buildAlongRouteSearchRequest,\n sendRequest: post,\n parseResponse: parseAlongRouteSearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport { alongRouteSearchTemplate } from './alongRouteSearchTemplate';\nimport type { AlongRouteSearchParams, AlongRouteSearchResponse } from './types';\n\n/**\n * Search for places along a route, ranked by detour cost.\n *\n * Returns POIs reachable within a given detour budget from the provided route geometry.\n * Results are sorted by detour time (or offset along the route) so the most\n * accessible stops appear first.\n *\n * @remarks\n * This service is ideal for trip planning use cases:\n * - Find fuel or EV charging stations along the way\n * - Locate food stops or rest areas near the route\n * - Surface services reachable within a time budget\n *\n * The `route` is typically sourced from a {@link calculateRoute} result.\n *\n * @param params Along-route search parameters including the route geometry and detour budget\n *\n * @returns Promise resolving to a collection of matching places sorted by detour cost\n *\n * @example\n * ```typescript\n * // Find coffee shops within a 5-minute detour\n * const results = await alongRouteSearch({\n * route: routeResult.features[0].geometry,\n * maxDetourTimeSeconds: 300,\n * query: 'coffee',\n * });\n *\n * // EV chargers with a 10-minute budget, sorted by position on route\n * const chargers = await alongRouteSearch({\n * route: routeResult.features[0].geometry,\n * maxDetourTimeSeconds: 600,\n * filters: { poiCategories: ['CHARGING_LOCATION'] },\n * sortBy: 'detourOffset',\n * });\n * ```\n *\n * @see [Along Route Search API Documentation](https://docs.tomtom.com/search-api/documentation/tomtom-orbis-maps/search-service/along-route-search)\n *\n * @ignore (exposed via 'search')\n */\nexport const alongRouteSearch = async (params: AlongRouteSearchParams): Promise<AlongRouteSearchResponse> =>\n callService(params, alongRouteSearchTemplate, 'AlongRouteSearch');\n","import { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\n\n/**\n * @ignore\n */\nexport const evChargingStationsAvailabilityRequestSchema = commonServiceRequestSchema.extend({\n id: z.string(),\n});\n","import type { LatestRequest, LatestRequestResult } from './types/latestRequest';\n\nconst notCurrent: LatestRequestResult<never> = { current: false };\n\n/**\n * Creates a slot that keeps only the latest request: starting one aborts its predecessor, and an\n * answer that arrives after a newer request or a `cancel` is dropped rather than returned.\n *\n * @remarks\n * Use one per thing that can be superseded — a click lookup, a search box, a refetch on map move.\n * Aborting stops the network work; checking again once the answer is in also catches a `cancel`\n * that arrives after the response but before the caller paints it.\n *\n * @example\n * ```ts\n * const lookup = createLatestRequest();\n *\n * map.mapLibreMap.on('click', async (event) => {\n * const result = await lookup.run((signal) => reverseGeocode({ position: event.lngLat.toArray(), signal }));\n * if (!result.current) return;\n * showAddress(result.value);\n * });\n * clearButton.addEventListener('click', lookup.cancel);\n * ```\n *\n * @see [Keeping only the latest request](https://docs.tomtom.com/maps-sdk-js/guides/services/request-options-and-errors#keeping-only-the-latest-request)\n *\n * @group Shared\n */\nexport const createLatestRequest = (): LatestRequest => {\n let controller: AbortController | undefined;\n\n const cancel = () => {\n controller?.abort();\n controller = undefined;\n };\n\n const run: LatestRequest['run'] = async (request) => {\n cancel();\n const ownController = new AbortController();\n controller = ownController;\n const { signal } = ownController;\n\n try {\n const value = await request(signal);\n return signal.aborted ? notCurrent : { current: true, value };\n } catch (error) {\n if (signal.aborted) return notCurrent;\n\n throw error;\n } finally {\n if (controller === ownController) controller = undefined;\n }\n };\n\n return { run, cancel, isRunning: () => controller !== undefined };\n};\n","import type { Avoidable, HasBBox, TravelMode } from '@tomtom-org/maps-sdk/core';\nimport { VehicleParameters } from './vehicleParams';\n\n/**\n * Available route types, where each type specifies the type of optimization used when calculating routes:\n * * **fast**: Route calculation is optimized by travel time, while keeping the routes sensible. For example, the calculation may avoid shortcuts along inconvenient side roads or long detours that only save very little time.\n * * **short**: Route calculation is optimized such that a good compromise between small travel time and short travel distance is achieved.\n * * **efficient**: Route calculation is optimized such that a good compromise between small travel time and low fuel or energy consumption is achieved.\n * * **thrilling**: Route calculation is optimized such that routes include interesting or challenging roads and use as few motorways as possible.\n * There is a limit of 900km on routes planned with routeType=thrilling.\n *\n * @group Routing\n */\nexport const routeTypes = ['fast', 'short', 'efficient', 'thrilling'] as const;\n\n/**\n * Route optimization strategy for route calculation.\n *\n * Determines what the routing engine optimizes for when calculating the route.\n * Each type produces different routes suited to different use cases.\n *\n * @remarks\n * **Route Type Strategies:**\n *\n * - **`fast`**: Minimize travel time while maintaining practicality\n * - Prefers major roads and highways\n * - Avoids unnecessary detours and shortcuts on minor roads\n * - Best for most everyday use cases (commuting, business travel)\n *\n * - **`short`**: Balance between time and distance\n * - Good compromise between speed and mileage\n * - May use smaller roads to save distance\n * - Useful for short trips or when fuel costs matter\n *\n * - **`efficient`**: Minimize fuel or energy consumption\n * - Optimizes for least energy use\n * - Considers vehicle consumption model\n * - Avoids rapid acceleration/deceleration\n * - Best used with vehicle consumption parameters\n *\n * - **`thrilling`**: Scenic and engaging routes\n * - Prefers curvy, interesting roads\n * - Minimizes motorway usage\n * - **Limited to 900km maximum route length**\n * - Ideal for motorcycle rides or scenic drives\n *\n * @example\n * ```typescript\n * // Fastest route for commuting\n * const routeType: RouteType = 'fast';\n *\n * // Most fuel-efficient route for long trip\n * const ecoRoute: RouteType = 'efficient';\n *\n * // Scenic route for leisure\n * const scenicRoute: RouteType = 'thrilling';\n * ```\n *\n * @group Routing\n */\nexport type RouteType = (typeof routeTypes)[number];\n\n/**\n * Traffic consideration mode for route calculation.\n *\n * Controls how traffic conditions are factored into routing and travel time estimates.\n *\n * @remarks\n * **Traffic Modes:**\n *\n * - **`live`**: Real-time + historical traffic\n * - Includes current traffic jams and incidents\n * - Considers short-term and long-term road closures\n * - Most accurate for immediate departures\n * - Updates with current conditions\n *\n * - **`historical`**: Typical traffic patterns only\n * - Based on historical data for time of day/week\n * - Ignores current traffic conditions\n * - Good for future trip planning\n * - More predictable for scheduled departures\n *\n * @example\n * ```typescript\n * // Route considering current traffic (departing now)\n * const trafficMode: TrafficInput = 'live';\n *\n * // Route based on typical patterns (planning ahead)\n * const plannedRoute: TrafficInput = 'historical';\n * ```\n *\n * @group Routing\n */\nexport type TrafficInput = 'live' | 'historical';\n\n/**\n * Cost model criteria for route optimization.\n *\n * Defines routing preferences and constraints that influence path selection.\n * Combines route type, traffic consideration, and avoidance criteria to determine\n * what makes a route \"better\" in the eyes of the routing engine.\n *\n * @remarks\n * The cost model affects how the route path is calculated, but does not affect what other data the response includes.\n *\n * The cost model balances multiple factors:\n * - Time efficiency\n * - Distance\n * - Fuel/energy consumption\n * - User preferences (avoid tolls, ferries, etc.)\n * - Traffic conditions\n *\n * @example\n * ```typescript\n * // Fast route avoiding tolls\n * const costModel: CostModel = {\n * routeType: 'fast',\n * traffic: 'live',\n * avoid: ['tollRoads']\n * };\n *\n * // Eco-friendly route avoiding highways\n * const ecoCostModel: CostModel = {\n * routeType: 'efficient',\n * traffic: 'historical',\n * avoid: ['motorways', 'ferries']\n * };\n *\n * // Scenic route for leisure\n * const scenicCostModel: CostModel = {\n * routeType: 'thrilling',\n * avoid: ['motorways', 'tollRoads']\n * };\n * ```\n *\n * @group Routing\n */\nexport type CostModel = {\n /**\n * Specifies something that the route calculation should try to avoid when determining the route.\n * @default None\n */\n avoid?: Avoidable[];\n\n /**\n * Rectangular areas for the routing engine to bypass, each expressed as a {@link HasBBox}.\n *\n * Up to 10 rectangles are supported. Useful for avoiding construction zones,\n * restricted areas, or known congestion points.\n *\n * @remarks\n * Constraints per rectangle:\n * - Maximum size: ~160×160 km\n * - Cannot cross the 180th meridian\n * - Latitude must be between −80° and +80°\n *\n * @example\n * ```typescript\n * // Avoid an area using a BBox array [west, south, east, north]\n * avoidAreas: [\n * [2.265938, 48.81851, 2.41115, 48.90309]\n * ]\n * ```\n */\n avoidAreas?: HasBBox[];\n\n /**\n * Decides how traffic is considered for computing routes.\n *\n * Possible values are:\n * * live: In addition to historical travel times, routing and estimated travel time\n * consider traffic jams and short- and long-term closures during the travel time window.\n * * historical: Routing and estimated travel time consider historical travel times and long term closures.\n * Traffic jams and short-term closures during the travel time window do not influence routing or travel time.\n *\n * @default live\n *\n * @remarks\n * This setting does not affect whether live traffic is included in the response or not.\n * * Considering historical traffic in cost model does not mean live traffic is not included in the response.\n */\n traffic?: TrafficInput;\n\n /**\n * Specifies the type of optimization used when calculating routes.\n * Possible values are:\n *\n * * **fast**: Route calculation is optimized by travel time, while keeping the routes sensible. For example, the calculation may avoid shortcuts along inconvenient side roads or long detours that only save very little time.\n * * **short**: Route calculation is optimized such that a good compromise between small travel time and short travel distance is achieved.\n * * **efficient**: Route calculation is optimized such that a good compromise between small travel time and low fuel or energy consumption is achieved.\n * * **thrilling**: Route calculation is optimized such that routes include interesting or challenging roads and use as few motorways as possible.\n * There is a limit of 900km on routes planned with routeType=thrilling.\n * @default fast\n */\n routeType?: RouteType;\n};\n\ntype DepartArriveOption = 'departAt' | 'arriveBy';\n\n/**\n * Departure or arrival time specification for route planning.\n *\n * Allows specifying either when to depart from the origin or when to arrive at the destination.\n * The routing engine calculates the route optimized for that specific time window, considering\n * traffic patterns for that time of day.\n *\n * @typeParam Option - Whether this specifies departure or arrival time\n *\n * @remarks\n * Traffic conditions vary significantly by:\n * - Time of day (rush hour vs off-peak)\n * - Day of week (weekday vs weekend)\n * - Special events or holidays\n *\n * Specifying a departure or arrival time enables the router to:\n * - Use appropriate traffic data for that time\n * - Plan around rush hour or quiet periods\n * - Calculate accurate arrival/departure times\n * - Account for time-dependent road restrictions\n *\n * **Important Notes:**\n * - Times are processed in the timezone of the origin/destination\n *\n * @example\n * ```typescript\n * // Depart at specific time (morning commute)\n * const departParams: DepartArriveParams = {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * };\n *\n * // Arrive by specific time (catch a flight)\n * const arriveParams: DepartArriveParams = {\n * option: 'arriveBy',\n * date: new Date('2025-10-20T14:00:00Z')\n * };\n *\n * // Plan route avoiding rush hour\n * const offPeakDepart: DepartArriveParams = {\n * option: 'departAt',\n * date: new Date('2025-10-20T10:30:00Z') // After morning rush\n * };\n * ```\n *\n * @group Routing\n */\nexport type DepartArriveParams<Option extends DepartArriveOption = DepartArriveOption> = {\n /**\n * Whether to specify a departure or arrival time.\n *\n * @remarks\n * - `departAt`: Calculate route from this departure time forward\n * - `arriveBy`: Calculate route backward to arrive by this time\n */\n option: Option;\n\n /**\n * The date and time to depart or arrive.\n *\n * @remarks\n * If past dates are supplied or dates that are impossible to achieve\n * (e.g., an imminent arrival date for a very long route), the system\n * will default to departing immediately.\n *\n * Times should be specified in ISO 8601 format or as JavaScript Date objects.\n */\n date: Date;\n};\n\n/**\n * Common parameters shared across all routing service requests.\n *\n * These parameters configure how routes are calculated, including optimization\n * strategy, vehicle characteristics, timing constraints, and travel preferences.\n * They provide a consistent interface across different routing services.\n *\n * @remarks\n * Most routing services (calculateRoute, calculateMatrixRoute, calculateReachableRange)\n * accept these parameters to customize route calculation. They control:\n * - What to optimize for (time, distance, fuel)\n * - Vehicle constraints and capabilities\n * - When to travel (affecting traffic)\n * - What features to avoid\n * - Mode of transportation\n *\n * **Service Compatibility:**\n * - {@link calculateRoute}: All parameters supported\n * - {@link calculateMatrixRoute}: Subset of parameters\n * - {@link calculateReachableRange}: Subset of parameters\n *\n * @example\n * ```typescript\n * // Standard car route avoiding tolls\n * const routingParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'fast',\n * traffic: 'live',\n * avoid: ['tollRoads']\n * },\n * travelMode: 'car',\n * when: {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * }\n * };\n *\n * // Electric vehicle route with consumption model\n * const evRoutingParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'efficient',\n * traffic: 'live'\n * },\n * travelMode: 'car',\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * dimensions: {\n * weightKG: 2000\n * },\n * engine: {\n * consumption: {\n * speedsToConsumptionsKWH: [\n * { speedKMH: 50, consumptionUnitsPer100KM: 15 },\n * { speedKMH: 90, consumptionUnitsPer100KM: 18 },\n * { speedKMH: 120, consumptionUnitsPer100KM: 22 }\n * ]\n * }\n * }\n * },\n * state: {\n * currentChargeInkWh: 60\n * }\n * }\n * };\n *\n * // Heavy, speed-limited vehicle, described by weight alone (see `VehicleDimensions`)\n * const heavyVehicleParams: CommonRoutingParams = {\n * costModel: {\n * routeType: 'short',\n * avoid: ['tollRoads', 'ferries']\n * },\n * vehicle: {\n * model: {\n * dimensions: {\n * weightKG: 40000\n * }\n * },\n * restrictions: {\n * maxSpeedKMH: 90\n * }\n * }\n * };\n * ```\n *\n * @see [Common Routing Parameters Documentation](https://docs.tomtom.com/routing-api/documentation/routing/common-routing-parameters)\n *\n * @group Routing\n */\nexport type CommonRoutingParams = {\n /**\n * Criteria that specifies what paths to prefer during routing.\n */\n costModel?: CostModel;\n\n /**\n * The primary means of transportation to be used while routing.\n *\n * The travel mode for the requested route.\n * Note that the requested travelMode may not be available for the entire route. Where\n * the requested travelMode is not available for a particular section, the element of the\n * response for that section will be 'other'.\n * @default None\n */\n travelMode?: TravelMode;\n\n /**\n * Specifies when to depart or arrive.\n * If past dates are supplied or in a way that are impossible to achieve\n * (e.g. an imminent arrival date for a long route), then it will default to departing now.\n * @default depart now\n */\n when?: DepartArriveParams;\n\n /**\n * Parameters for the vehicle that will be used to drive the route.\n */\n vehicle?: VehicleParameters;\n};\n","/**\n * Whether the vehicle carries an electronic toll collection transponder.\n *\n * Determines which toll roads the routing engine may use: some tolls can only be paid by\n * transponder, others only without one.\n *\n * @group Vehicle\n */\nexport const tollTransponderOptions = ['all', 'unknown', 'none'] as const;\n\n/**\n * Whether the vehicle carries an electronic toll collection transponder.\n *\n * @remarks\n * - `all` — the vehicle has a transponder accepted by every toll operator on the route\n * - `unknown` — transponder availability is unknown, so no assumption is made\n * - `none` — the vehicle has no transponder\n *\n * @example\n * ```typescript\n * const transponder: TollTransponder = 'all';\n * ```\n *\n * @group Vehicle\n */\nexport type TollTransponder = (typeof tollTransponderOptions)[number];\n\n/**\n * Parameters for a vehicle which are related to restrictions applied during route planning.\n */\nexport type VehicleRestrictions = {\n /**\n * Vehicle restrictions applied during route planning.\n */\n restrictions?: {\n /**\n * Maximum speed of the vehicle in kilometers/hour.\n * * Must have a value in the range [0, 250].\n * * A value of 0 means that an appropriate value for the vehicle will be determined and applied during route planning.\n *\n * @default 0\n */\n maxSpeedKMH?: number;\n\n /**\n * Whether the vehicle carries an electronic toll collection transponder.\n *\n * @remarks\n * Affects which toll roads are considered usable: a toll that can only be paid by\n * transponder is avoided when this is `none`, and a toll that cannot be paid by\n * transponder is avoided when this is `all`.\n *\n * @default 'unknown'\n *\n * @example\n * ```typescript\n * restrictions: {\n * tollTransponder: 'all'\n * }\n * ```\n */\n tollTransponder?: TollTransponder;\n };\n};\n","import type { ParseResponseError } from '../shared';\nimport { SDKServiceError } from '../shared';\n\n/**\n * @ignore\n * @param apiError\n * @param serviceName\n */\nexport const parseEVChargingStationsAvailabilityResponseError: ParseResponseError = (apiError, serviceName) => {\n const errorMessage = apiError.data?.detailedError?.message ?? apiError.message;\n return new SDKServiceError(errorMessage, serviceName, apiError.status);\n};\n","import { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { appendCommonParams } from '../shared/request/requestBuildingUtils';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\nconst buildUrlBasePath = (params: ChargingStationsAvailabilityParams): string =>\n resolvePlacesEndpointUrl(params, 'ev/id');\n\n/**\n * Default method for building ev charging stations availability request from {@link ChargingStationsAvailabilityParams}\n * @param params The charging availability parameters, with global configuration already merged into them.\n *\n * @remarks\n * Unlike the other places services, this one keeps `key=` and `apiVersion=` in the query string.\n * `/maps/orbis/places/ev/id` answers 401 to a header-only credential, while `search`,\n * `poiCategories`, `geocode`, `place` and `additionalData` all accept one — probed against\n * production. Moving this one to headers breaks it outright rather than subtly, so it stays until\n * the endpoint catches up.\n */\nexport const buildEVChargingStationsAvailabilityRequest = (params: ChargingStationsAvailabilityParams): URL => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n appendCommonParams(urlParams, params);\n urlParams.append('id', params.id);\n return url;\n};\n","import type { ChargingStationsAvailability } from '@tomtom-org/maps-sdk/core';\nimport { toChargingStations } from '../shared/ev';\nimport { parseOpeningHours } from '../shared/searchResultParsing';\nimport { toChargingPointAvailability, toConnectorBasedAvailabilities } from './connectorAvailability';\nimport type { ChargingStationsAvailabilityResponseAPI } from './types/apiTypes';\n\n/**\n * Default method for parsing ev charging stations availability from {@link ChargingStationsAvailability}\n * @param apiResponse\n */\nexport const parseEVChargingStationsAvailabilityResponse = (\n apiResponse: ChargingStationsAvailabilityResponseAPI,\n): ChargingStationsAvailability | undefined => {\n const result = apiResponse.results?.[0];\n if (!result) {\n return undefined;\n }\n\n const chargingStations = toChargingStations(result.chargingStations);\n return {\n id: result.id,\n accessType: result.accessType,\n chargingStations,\n chargingPointAvailability: toChargingPointAvailability(chargingStations),\n connectorAvailabilities: toConnectorBasedAvailabilities(chargingStations),\n ...(result.openingHours && { openingHours: parseOpeningHours(result.openingHours) }),\n };\n};\n","import type { ChargingStationsAvailability } from '@tomtom-org/maps-sdk/core';\nimport type { ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { evChargingStationsAvailabilityRequestSchema } from './evChargingStationsAvailabilityRequestSchema';\nimport { parseEVChargingStationsAvailabilityResponseError } from './evChargingStationsAvailabilityResponseErrorParser';\nimport { buildEVChargingStationsAvailabilityRequest } from './requestBuilder';\nimport { parseEVChargingStationsAvailabilityResponse } from './responseParser';\nimport type { ChargingStationsAvailabilityResponseAPI } from './types/apiTypes';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\n/**\n * EV Charging Stations Availability service template type.\n * @ignore\n */\nexport type EVChargingStationsAvailabilityTemplate = ServiceTemplate<\n ChargingStationsAvailabilityParams,\n URL,\n ChargingStationsAvailabilityResponseAPI,\n ChargingStationsAvailability | undefined\n>;\n\n/**\n * EV Charging Stations Availability service template main implementation.\n * @ignore\n */\nexport const evChargingStationsAvailabilityTemplate: EVChargingStationsAvailabilityTemplate = {\n requestValidation: { schema: evChargingStationsAvailabilityRequestSchema },\n buildRequest: buildEVChargingStationsAvailabilityRequest,\n sendRequest: get,\n parseResponse: parseEVChargingStationsAvailabilityResponse,\n parseResponseError: parseEVChargingStationsAvailabilityResponseError,\n};\n","import type {\n ChargingPark,\n ChargingParkWithAvailability,\n ChargingStationsAvailability,\n CommonPlaceProps,\n EVChargingStationWithAvailabilityPlaceProps,\n Place,\n Places,\n} from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport { SDKAbortError } from '../shared/errors';\nimport { callService } from '../shared/serviceTemplate';\nimport { toConnectorCounts } from './connectorAvailability';\nimport { evChargingStationsAvailabilityTemplate } from './evChargingStationsAvailabilityTemplate';\nimport type { ChargingStationsAvailabilityParams } from './types/evChargingStationsAvailabilityParams';\n\n/**\n * Get real-time availability of electric vehicle charging stations.\n *\n * Provides current operational status of charging points and connectors at EV charging parks,\n * enabling drivers to find available chargers before arriving at a location.\n *\n * @remarks\n * **Access**: needs an API key with EV Charging Stations Availability access, unavailable on a Freemium or\n * Pay As You Grow (PAYG) basis: [request it from TomTom Sales](https://www.tomtom.com/contact-sales/?source_app=docs-portal&source_product=ev-search).\n *\n * Key information returned:\n * - **Point-level status**: Available, Occupied, Reserved, Out of Service\n * - **Connector details**: Power ratings, plug types, current availability\n * - **Aggregated counts**: Quick overview of available vs occupied chargers\n * - **Access information**: Public, private, or restricted access\n * - **Opening hours**: When the charging facility is accessible\n *\n * Returns availability records, not places. To show stations with their availability, pass the\n * places to {@link getPlacesWithEVAvailability} instead, and its result to `PlacesModule.show()`.\n *\n * @param params - Charging availability parameters with station ID\n *\n * @returns Promise resolving to charging station availability information\n *\n * @example\n * ```typescript\n * // Get availability for a specific charging park\n * const availability = await evChargingStationsAvailability({\n * apiKey: 'YOUR_API_KEY',\n * id: 'charging-park-id-123'\n * });\n *\n * // Check how many chargers are available\n * const availableCount = availability?.chargingPointAvailability.statusCounts.Available ?? 0;\n * console.log(`${availableCount} chargers available`);\n *\n * // CCS connectors, one entry per power level\n * const ccsConnectors = availability?.connectorAvailabilities.filter(\n * ({ connector }) => connector.type === 'IEC62196Type2CCS'\n * );\n * ```\n *\n * @see [EV Charging Availability API](https://docs.tomtom.com/search-api/documentation)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [EV Charging Stations Availability Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/ev-charging-stations-availability)\n *\n * @group EV Charging\n */\nexport const evChargingStationsAvailability = async (\n params: ChargingStationsAvailabilityParams,\n): Promise<ChargingStationsAvailability | undefined> =>\n callService(params, evChargingStationsAvailabilityTemplate, 'EVChargingStationsAvailability');\n\n/**\n * Enhance a place with real-time EV charging availability data.\n *\n * Fetches availability information for an EV charging station and merges it into\n * the place properties.\n *\n * @remarks\n * Needs the same API key access as {@link evChargingStationsAvailability}. A failed request,\n * including one from a key without that access, is logged and resolves to `undefined`. A\n * cancellation is the caller's own decision rather than a data gap, so it rejects the call instead.\n *\n * @param place - The place to enhance with availability data\n * @param commonParams - Common service parameters (e.g. `apiKey`, `onAPIRequest`, `signal`) forwarded to the availability request\n *\n * @returns Promise resolving to the place with merged availability information\n * @returns undefined if no availability data is present for the `place` input parameter.\n * @throws {@link SDKAbortError} when `commonParams.signal` is aborted\n *\n * @example\n * ```typescript\n * // After search, enhance place with availability\n * const searchResult = await discoverPlaces({ query: 'EV charging', ... });\n * const place = searchResult.features[0];\n *\n * const enhancedPlace = await getPlaceWithEVAvailability(place);\n * if (enhancedPlace) {\n * const { count, statusCounts } = enhancedPlace.properties.chargingPark.availability.chargingPointAvailability;\n * console.log(`${statusCounts.Available ?? 0} of ${count} chargers available`);\n * }\n * ```\n *\n * @group EV Charging\n */\nexport const getPlaceWithEVAvailability = async <P extends CommonPlaceProps = CommonPlaceProps>(\n place: Place<P>,\n commonParams?: Omit<ChargingStationsAvailabilityParams, 'id'>,\n): Promise<Place<EVChargingStationWithAvailabilityPlaceProps> | undefined> => {\n const { signal } = commonParams ?? {};\n // Checked before the id, so a cancellation rejects even for a place that needs no request\n if (signal?.aborted) {\n throw new SDKAbortError('EVChargingStationsAvailability', signal.reason);\n }\n\n const availabilityId = place.properties.dataSources?.chargingAvailability?.id;\n if (!availabilityId) {\n return undefined;\n }\n try {\n const availability = await evChargingStationsAvailability({ ...commonParams, id: availabilityId });\n const poi = place.properties.poi;\n return availability\n ? {\n ...place,\n properties: {\n ...place.properties,\n // We override poi opening hours with the ones from the EV call, which might be better supported:\n ...(poi && { poi: { ...poi, openingHours: availability.openingHours } }),\n chargingPark: {\n connectors: toConnectorCounts(availability.chargingStations),\n ...place.properties.chargingPark,\n availability,\n },\n },\n }\n : undefined;\n } catch (error) {\n // A cancellation is the caller's decision, not a data gap, so it is not swallowed\n if (error instanceof SDKAbortError) throw error;\n // (Likely a QPS limit error)\n console.error(error);\n return undefined;\n }\n};\n\n/**\n * Enhance multiple places with real-time EV charging availability data.\n *\n * Fetches availability information for all EV charging stations in a collection\n * and merges it into their properties. Non-EV places and EV stations without availability data are returned unchanged.\n *\n * @remarks\n * **Important**: Availability requests are made sequentially to avoid exceeding\n * API rate limits (QPS - Queries Per Second). For large result sets, this may\n * take some time. Pass a `signal` to stop early: aborting it cancels the in-flight\n * request, skips the remaining ones and rejects with {@link SDKAbortError}.\n *\n * Needs the same API key access as {@link evChargingStationsAvailability}. A place whose request\n * fails, including for a key without that access, counts as having unknown availability.\n *\n * @param places - Collection of places to enhance\n * @param options - Configuration options, including common service parameters (e.g. `apiKey`, `onAPIRequest`, `signal`) forwarded to each availability request\n *\n * @returns Promise resolving to places collection with merged availability\n *\n * @example\n * ```typescript\n * // Search for charging stations and add availability\n * const results = await discoverPlaces({\n * query: 'EV charging',\n * geoBias: { position: [4.9, 52.3], radiusMeters: 5000 }\n * });\n *\n * const withAvailability = await getPlacesWithEVAvailability(results, {\n * excludeIfAvailabilityUnknown: true // Filter out stations with unknown availability\n * });\n *\n * // Display only stations with known availability\n * withAvailability.features.forEach(place => {\n * const { count, statusCounts } = place.properties.chargingPark.availability.chargingPointAvailability;\n * console.log(`${place.properties.poi?.name}: ${statusCounts.Available ?? 0} of ${count} chargers available`);\n * });\n * ```\n *\n * @group EV Charging\n */\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n excludeIfAvailabilityUnknown: true;\n },\n): Promise<Places<EVChargingStationWithAvailabilityPlaceProps>>;\n\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n /**\n * If true, places with unknown availability will be filtered out. Otherwise, they will be included.\n * @default false\n */\n excludeIfAvailabilityUnknown?: false;\n },\n): Promise<Places<P | EVChargingStationWithAvailabilityPlaceProps>>;\n\nexport async function getPlacesWithEVAvailability<P extends CommonPlaceProps = CommonPlaceProps>(\n places: Places<P>,\n options?: Omit<ChargingStationsAvailabilityParams, 'id'> & {\n excludeIfAvailabilityUnknown?: boolean;\n },\n): Promise<Places<P | EVChargingStationWithAvailabilityPlaceProps>> {\n const { excludeIfAvailabilityUnknown, ...commonParams } = options ?? {};\n const enhancedPlaces: Array<Place<P> | Place<EVChargingStationWithAvailabilityPlaceProps>> = [];\n for (const place of places.features) {\n // (We fetch the availabilities sequentially on purpose to prevent QPS limit errors)\n const placeWithAvailability = await getPlaceWithEVAvailability(place, commonParams);\n if (placeWithAvailability) {\n enhancedPlaces.push(placeWithAvailability);\n } else if (!excludeIfAvailabilityUnknown) {\n enhancedPlaces.push(place);\n }\n }\n return { ...places, features: enhancedPlaces, bbox: bboxFromGeoJSON(enhancedPlaces) };\n}\n\n/**\n * Type guard to check if a ChargingPark has availability data.\n *\n * @param chargingPark - The charging park to check\n * @returns True if the charging park has availability data\n *\n * @group EV Charging\n */\nexport const hasChargingAvailability = (\n chargingPark: ChargingPark | ChargingParkWithAvailability | undefined,\n): chargingPark is ChargingParkWithAvailability =>\n Boolean(chargingPark && 'availability' in chargingPark && chargingPark.availability);\n","/**\n * The single-result form of a places service: takes a bare query or the params bar `limit`, and\n * returns the first feature or throws.\n *\n * @ignore\n */\nexport const fetchFirstPlace = async <Params extends { query?: string }, Feature>(\n functionName: string,\n queryOrParams: string | Params,\n service: (params: (Params | { query: string }) & { limit: 1 }) => Promise<{ features: Feature[] }>,\n): Promise<Feature> => {\n const params = typeof queryOrParams === 'string' ? { query: queryOrParams } : queryOrParams;\n const [first] = (await service({ ...params, limit: 1 })).features;\n if (!first) throw new Error(`${functionName}: no results found for \"${params.query}\"`);\n return first;\n};\n","import { z } from 'zod';\nimport { hasBBoxSchema, hasLngLatSchema } from './geometriesSchema';\n\n/** The point arm on its own, for the endpoints that bias by point only. @ignore */\nexport const pointGeoBiasSchema = z.object({\n position: hasLngLatSchema,\n radiusMeters: z.number().optional(),\n});\n\n/** @ignore */\nexport const geoBiasSchema = z.union([pointGeoBiasSchema, z.object({ boundingBox: hasBBoxSchema })]);\n","import { z } from 'zod';\nimport { searchCursorSchema } from '../searchCursor';\nimport { geoBiasSchema } from './geoBiasSchema';\nimport { geographicFiltersSchema } from './searchFiltersSchema';\n\n/**\n * @ignore\n */\nexport const commonGeocodeAndFuzzySearchParamsSchema = z.object({\n typeahead: z.boolean().optional(),\n cursor: searchCursorSchema.optional(),\n geoBias: geoBiasSchema.optional(),\n filters: geographicFiltersSchema.optional(),\n});\n","import { z } from 'zod';\nimport { commonGeocodeAndFuzzySearchParamsSchema } from '../shared/schema/commonGeocodeAndFuzzySearchParamsSchema';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { commonPlacesParamsSchema } from '../shared/schema/commonPlacesParamsSchema';\nimport { geographicFiltersSchema, placeFiltersSchema } from '../shared/schema/searchFiltersSchema';\n\nconst geocodingFiltersSchema = geographicFiltersSchema.extend(placeFiltersSchema.pick({ geographyTypes: true }).shape);\n\n/**\n * @ignore\n */\nexport const geocodingRequestSchema = commonServiceRequestSchema\n .extend(commonPlacesParamsSchema.shape)\n .extend(commonGeocodeAndFuzzySearchParamsSchema.shape)\n .extend({ query: z.string(), filters: geocodingFiltersSchema.optional() });\n","import { isNil } from 'lodash-es';\nimport type { GetObject } from '../shared';\nimport { arrayToCSV } from '../shared/arrays';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport {\n appendByJoiningParamValue,\n appendGeoBiasParams,\n appendPlacesLanguageParam,\n buildPlacesRequestHeaders,\n} from '../shared/request/requestBuildingUtils';\nimport { appendSearchCursorParam } from '../shared/searchCursor';\nimport type { GeocodingParams } from './types/geocodingParams';\n\nconst buildUrlBasePath = (params: GeocodingParams): string => resolvePlacesEndpointUrl(params, 'geocode');\n\n/**\n * Default method for building geocoding request from {@link GeocodingParams}\n * @param params The geocoding parameters, with global configuration already merged into them.\n */\nexport const buildGeocodingRequest = (params: GeocodingParams): GetObject => {\n const url = new URL(`${buildUrlBasePath(params)}/${encodeURIComponent(params.query)}.json`);\n const urlParams = url.searchParams;\n appendPlacesLanguageParam(urlParams, params);\n // geocoding specific parameters:\n params.typeahead && urlParams.append('typeahead', String(params.typeahead));\n !isNil(params.limit) && urlParams.append('limit', String(params.limit));\n appendSearchCursorParam(urlParams, params.cursor, 'Geocode');\n appendGeoBiasParams(urlParams, params.geoBias);\n appendByJoiningParamValue(urlParams, 'countrySet', params.filters?.countries);\n params.extendedPostalCodesFor &&\n urlParams.append('extendedPostalCodesFor', arrayToCSV(params.extendedPostalCodesFor));\n params.mapcodes && urlParams.append('mapcodes', arrayToCSV(params.mapcodes));\n params.view && urlParams.append('view', params.view);\n appendByJoiningParamValue(urlParams, 'entityTypeSet', params.filters?.geographyTypes);\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import type { GeographyType, Place } from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON, bboxOnlyIfWithArea, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { omit } from 'lodash-es';\nimport { apiToGeoJSONBBox, latLonAPIToPosition } from '../shared/geometry';\nimport { parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { GeocodingResponseAPI, GeocodingResultAPI } from './types/apiTypes';\nimport type { GeocodingProps, GeocodingResponse } from './types/geocodingResponse';\n\nconst parseApiResult = (result: GeocodingResultAPI): Place<GeocodingProps> => {\n const { position, boundingBox, dist, entryPoints, addressRanges, entityType, id, ...rest } = result;\n\n return {\n type: 'Feature',\n geometry: toPointGeometry(latLonAPIToPosition(position)),\n ...(boundingBox && { bbox: apiToGeoJSONBBox(boundingBox) }),\n id,\n properties: {\n ...omit(rest, 'viewport'),\n ...(dist && { distance: dist }),\n ...(entityType && { geographyType: entityType.split(',') as GeographyType[] }),\n ...(entryPoints && {\n entryPoints: entryPoints.map((entrypoint) => ({\n ...entrypoint,\n position: latLonAPIToPosition(entrypoint.position),\n })),\n }),\n ...(addressRanges && {\n addressRanges: {\n ...addressRanges,\n from: latLonAPIToPosition(addressRanges.from),\n to: latLonAPIToPosition(addressRanges.to),\n },\n }),\n },\n };\n};\n\n/**\n * Default method for parsing geocoding request from {@link GeocodingResponse}\n * @param apiResponse\n */\nexport const parseGeocodingResponse = (apiResponse: GeocodingResponseAPI): GeocodingResponse => {\n const results = apiResponse.results;\n const features = results.map(parseApiResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: parseSummaryAPI(apiResponse.summary, { paginated: true }),\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { geocodingRequestSchema } from './geocodingRequestSchema';\nimport { buildGeocodingRequest } from './requestBuilder';\nimport { parseGeocodingResponse } from './responseParser';\nimport type { GeocodingResponseAPI } from './types/apiTypes';\nimport type { GeocodingParams } from './types/geocodingParams';\nimport type { GeocodingResponse } from './types/geocodingResponse';\n\n/**\n * Geocoding service template type.\n * @ignore\n */\nexport type GeocodingTemplate = ServiceTemplate<GeocodingParams, GetObject, GeocodingResponseAPI, GeocodingResponse>;\n\n/**\n * Geocoding service template main implementation.\n * @ignore\n */\nexport const geocodingTemplate: GeocodingTemplate = {\n requestValidation: { schema: geocodingRequestSchema },\n buildRequest: buildGeocodingRequest,\n sendRequest: get,\n parseResponse: parseGeocodingResponse,\n};\n","import type { Place } from '@tomtom-org/maps-sdk/core';\nimport { fetchFirstPlace } from '../shared/firstPlace';\nimport { callService } from '../shared/serviceTemplate';\nimport { geocodingTemplate } from './geocodingTemplate';\nimport type { GeocodingParams } from './types/geocodingParams';\nimport type { GeocodingProps, GeocodingResponse } from './types/geocodingResponse';\n\n/**\n * Convert addresses into geographic coordinates (geocoding).\n *\n * The Geocode service translates addresses and place names into geographic coordinates,\n * enabling you to position markers on maps, calculate routes, or perform spatial analysis.\n *\n * @remarks\n * This service is optimized for address lookup and does not return POIs (Points of Interest).\n * For POI search, use the {@link discoverPlaces} function instead.\n *\n * Features:\n * - Highly tolerant of typos and incomplete addresses\n * - Handles various address formats and components\n * - Supports street addresses, intersections, and cross streets\n * - Works with higher-level geographies (cities, counties, states, countries)\n * - Returns structured address components\n *\n * @param params - Geocoding parameters including the address query\n *\n * @returns Promise resolving to geocoded location results\n *\n * @example\n * ```typescript\n * // Geocode a complete address\n * const result = await geocode({\n * key: 'your-api-key',\n * query: '1600 Pennsylvania Avenue NW, Washington, DC'\n * });\n *\n * // Geocode with partial address\n * const partialResult = await geocode({\n * key: 'your-api-key',\n * query: 'Amsterdam, Netherlands'\n * });\n *\n * // Geocode with bias towards specific location\n * const biasedResult = await geocode({\n * key: 'your-api-key',\n * query: 'Main Street',\n * at: [4.9041, 52.3676], // Bias toward Amsterdam\n * limit: 5\n * });\n *\n * // Geocode an intersection\n * const intersection = await geocode({\n * key: 'your-api-key',\n * query: '5th Avenue & 42nd Street, New York'\n * });\n * ```\n *\n * @see [Geocode API Documentation](https://docs.tomtom.com/search-api/documentation/geocoding-service/geocode)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Geocoding Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/geocoding)\n *\n * @group Geocoding\n */\nexport const geocode = async (params: GeocodingParams): Promise<GeocodingResponse> =>\n callService(params, geocodingTemplate, 'Geocode');\n\n/**\n * Geocode a query and return the single best-matching place.\n *\n * Convenience wrapper around {@link geocode} that returns the top result directly.\n *\n * @param queryOrParams - Address or place name to geocode, or the full {@link GeocodingParams} (bar `limit`)\n * @returns Promise resolving to the best-matching place\n * @throws {Error} If no results are found for the query\n *\n * @remarks\n * Use {@link geocode} with a `limit` parameter when you need to handle the case\n * where no results are found without throwing.\n *\n * @example\n * ```typescript\n * const place = await geocodeOne('Amsterdam Centraal');\n * console.log(place.geometry.coordinates); // [lng, lat]\n *\n * const biased = await geocodeOne({ query: 'Main Street', geoBias: { position: [4.9041, 52.3676] }, signal });\n * ```\n *\n * @group Geocoding\n */\nexport const geocodeOne = async (\n queryOrParams: string | Omit<GeocodingParams, 'limit'>,\n): Promise<Place<GeocodingProps>> => fetchFirstPlace('geocodeOne', queryOrParams, geocode);\n","import { z } from 'zod';\nimport { featureCollectionSchema, featureSchema } from '../shared/schema/geometriesSchema';\n\nconst geometryDataRequestMandatory = z.object({\n geometries: z.union([\n featureCollectionSchema,\n z\n .array(z.union([z.string(), featureSchema]))\n .min(1)\n .max(20),\n ]),\n});\n\nconst geometryDataRequestOptional = z.object({\n zoom: z.number().min(0).max(22).optional(),\n});\n\nexport const geometryDataRequestSchema = geometryDataRequestMandatory.extend(geometryDataRequestOptional.shape);\n","import type { Place, Places } from '@tomtom-org/maps-sdk/core';\nimport type { GetObject } from '../shared';\nimport { arrayToCSV } from '../shared/arrays';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { appendOptionalParam, buildPlacesRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { GeometriesInput, GeometryParams } from './types/geometryDataParams';\n\nconst buildUrlBasePath = (params: GeometryParams): string => resolvePlacesEndpointUrl(params, 'additionalData.json');\n\nconst getGeometryIDs = (placesArray: Place[]): string[] =>\n placesArray.map((place) => place.properties.dataSources?.geometry?.id as string).filter((id) => id);\n\n// (@see geometryDataRequestSchema)\nconst appendGeometries = (urlParams: URLSearchParams, geometries: GeometriesInput | Places | Place[]): void => {\n let geometryIDs: string[];\n\n if (Array.isArray(geometries)) {\n // (assuming min and max length already validated)\n if (typeof geometries[0] === 'string') {\n geometryIDs = geometries as string[];\n } else {\n geometryIDs = getGeometryIDs(geometries as Place[]);\n }\n } else {\n // (assuming already validated as FeatureCollection)\n geometryIDs = getGeometryIDs(geometries.features);\n }\n\n urlParams.append('geometries', arrayToCSV(geometryIDs));\n};\n\n/**\n * Default function for building a geometry data request from {@link GeometryDataParams}\n * @param params The geometry data parameters, with global configuration already merged into them.\n */\nexport const buildGeometryDataRequest = (params: GeometryParams): GetObject => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n // (no language in this service, and the credentials travel as headers)\n appendGeometries(urlParams, params.geometries);\n appendOptionalParam(urlParams, 'geometriesZoom', params.zoom);\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import { bboxFromGeoJSON, generateId, PolygonFeature, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { GeometryDataResponseAPI } from './types/apiTypes';\n\n/**\n * Default geometry data API response parsing.\n * * The API response consists of an array, with a FeatureCollection (with only one feature) for each geometry.\n * * The parsed response consists of a fully-GeoJSON-compatible FeatureCollection with a Feature for each geometry.\n * * Each geometry ID is included in each GeoJSON feature \"id\" field.\n * @param apiResponse\n */\nexport const parseGeometryDataResponse = (apiResponse: GeometryDataResponseAPI): PolygonFeatures => {\n const features = apiResponse.additionalData\n .flatMap((data) =>\n (data.geometryData as PolygonFeatures)?.features.map((feature) => ({\n ...feature,\n id: feature.id ?? generateId(),\n bbox: bboxFromGeoJSON(feature.geometry),\n })),\n )\n .filter((feature) => feature) as PolygonFeature[];\n return {\n type: 'FeatureCollection',\n bbox: bboxFromGeoJSON(features),\n features,\n };\n};\n","import type { PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { geometryDataRequestSchema } from './geometryDataRequestSchema';\nimport { buildGeometryDataRequest } from './requestBuilder';\nimport { parseGeometryDataResponse } from './responseParser';\nimport type { GeometryDataResponseAPI } from './types/apiTypes';\nimport type { GeometryParams } from './types/geometryDataParams';\n\n/**\n * @ignore\n */\nexport type GeometryDataTemplate = ServiceTemplate<GeometryParams, GetObject, GeometryDataResponseAPI, PolygonFeatures>;\n\n/**\n * @ignore\n */\nexport const geometryDataTemplate: GeometryDataTemplate = {\n requestValidation: { schema: geometryDataRequestSchema },\n buildRequest: buildGeometryDataRequest,\n sendRequest: get,\n parseResponse: parseGeometryDataResponse,\n};\n","import type { CommonPlaceProps, Places, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { geometryDataTemplate } from './geometryDataTemplate';\nimport type { GeometryDataParams, GeometryParams, GeometryPlaceParams } from './types/geometryDataParams';\n\n/**\n * Merge our internal Places \"properties\" response with Geometry data\n * @param places\n * @param geometries\n * @returns FeatureCollection<Polygon | MultiPolygon>,\n */\nconst mergePlacesWithGeometries = (places: Places, geometries: PolygonFeatures): PolygonFeatures<CommonPlaceProps> => {\n const placesIdMap = places.features.reduce(\n (acc, place) => {\n const geometryId = place.properties.dataSources?.geometry?.id;\n\n if (geometryId) {\n acc[geometryId] = {\n ...place.properties,\n placeCoordinates: place.geometry.coordinates,\n };\n }\n return acc;\n },\n {} as Record<string, unknown>,\n );\n\n const features = geometries.features.map((feature) => {\n if (feature.id && placesIdMap[feature.id]) {\n return { ...feature, properties: placesIdMap[feature.id] };\n }\n\n return feature;\n });\n\n return {\n type: 'FeatureCollection',\n bbox: geometries.bbox,\n features,\n } as PolygonFeatures<CommonPlaceProps>;\n};\n\n/**\n * Retrieve polygon geometries representing geographic area boundaries.\n *\n * The Geometry Data service returns coordinate sets that define the outlines of\n * geographic areas such as cities, countries, administrative regions, or POI footprints.\n * These polygons enable visualization of area boundaries, spatial analysis, and\n * geofencing applications.\n *\n * @remarks\n * Key features:\n * - **Batch requests**: Fetch up to 20 geometries in a single call\n * - **Multiple scales**: From countries down to building footprints\n * - **Place integration**: Can merge with place data for enriched results\n * - **Standard GeoJSON**: Returns standard Polygon/MultiPolygon features\n *\n * Common use cases:\n * - Display city or country boundaries on maps\n * - Show POI building footprints\n * - Create geofences for spatial queries\n * - Visualize administrative divisions\n * - Calculate areas and spatial relationships\n *\n * Returns polygons, not places. Pass the result to `GeometriesModule.show()`,\n * not `PlacesModule.show()`. The `geometryId` comes from a prior search result.\n *\n * @param params - Geometry parameters with IDs or places to fetch boundaries for\n *\n * @returns Promise resolving to polygon features representing area boundaries\n *\n * @example\n * ```typescript\n * // Fetch geometry by ID\n * const cityBoundary = await geometryData({\n * key: 'your-api-key',\n * geometries: ['geometry-id-123']\n * });\n *\n * // Fetch multiple geometries at once\n * const boundaries = await geometryData({\n * key: 'your-api-key',\n * geometries: ['country-id-1', 'city-id-2', 'poi-id-3']\n * });\n *\n * // Fetch and merge with place data\n * const searchResults = await discoverPlaces({ query: 'Amsterdam' });\n * const withBoundaries = await geometryData({\n * key: 'your-api-key',\n * geometries: searchResults // Places with geometry IDs\n * });\n * // Result includes both place properties and polygon boundaries\n * ```\n *\n * @see [Geometry Data API Documentation](https://docs.tomtom.com/search-api/documentation/additional-data-service/additional-data)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Geometry Data Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/geometry-data)\n *\n * @group Geometry\n */\nexport async function geometryData(params: GeometryDataParams): Promise<PolygonFeatures>;\nexport async function geometryData(params: GeometryPlaceParams): Promise<PolygonFeatures<CommonPlaceProps>>;\nexport async function geometryData(params: GeometryParams) {\n const geometryResult = await callService(params, geometryDataTemplate, 'GeometryData');\n\n // If params.geometries is a FeatureCollection(Place), the properties will be merged with geometry results.\n if (!Array.isArray(params.geometries) && params.geometries.type === 'FeatureCollection') {\n return mergePlacesWithGeometries(params.geometries, geometryResult);\n }\n\n return geometryResult;\n}\n","import { views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\n\nconst placeDetailsRequestMandatory = z.object({\n entityId: z.string(),\n});\n\nconst placeDetailsRequestOptional = z.object({\n mapcodes: z.array(z.string()).optional(),\n view: z.enum(views).optional(),\n openingHours: z.string().optional(),\n timeZone: z.string().optional(),\n relatedPois: z.string().optional(),\n});\n\n/**\n * @ignore\n */\nexport const placeDetailsRequestSchema = placeDetailsRequestMandatory.extend(placeDetailsRequestOptional.shape);\n","import type { CommonServiceParams, GetObject } from '../shared';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport {\n appendByJoiningParamValue,\n appendEVModelVersionParam,\n appendOptionalParam,\n appendPlacesLanguageParam,\n buildPlacesRequestHeaders,\n} from '../shared/request/requestBuildingUtils';\nimport type { PlaceDetailsParams } from './types';\n\nconst buildUrlBasePath = (params: CommonServiceParams): string => resolvePlacesEndpointUrl(params, 'place.json');\n\n/**\n * Default method for building place by id request from {@link PlaceDetailsParams}\n * @param params The place by id parameters, with global configuration already merged into them.\n */\nexport const buildPlaceDetailsRequest = (params: PlaceDetailsParams): GetObject => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n const urlParams = url.searchParams;\n appendPlacesLanguageParam(urlParams, params);\n appendOptionalParam(urlParams, 'entityId', params.entityId);\n appendByJoiningParamValue(urlParams, 'mapcodes', params.mapcodes);\n appendOptionalParam(urlParams, 'view', params.view);\n appendOptionalParam(urlParams, 'openingHours', params.openingHours);\n appendOptionalParam(urlParams, 'timeZone', params.timeZone);\n appendOptionalParam(urlParams, 'relatedPois', params.relatedPois);\n appendEVModelVersionParam(urlParams);\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import { parseSearchAPIResult } from '../shared/searchResultParsing';\nimport type { PlaceDetailsResponse, PlaceDetailsResponseAPI } from './types';\n\n/**\n * Default method for parsing place by id response.\n * @param apiResponse The place by id response.\n */\nexport const parsePlaceDetailsResponse = (apiResponse: PlaceDetailsResponseAPI): PlaceDetailsResponse =>\n apiResponse.results?.length ? parseSearchAPIResult(apiResponse.results[0]) : undefined;\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { placeDetailsRequestSchema } from './placeDetailsSchema';\nimport { buildPlaceDetailsRequest } from './requestBuilder';\nimport { parsePlaceDetailsResponse } from './responseParser';\nimport type { PlaceDetailsParams, PlaceDetailsResponse, PlaceDetailsResponseAPI } from './types';\n\n/**\n * Place details template type.\n * @ignore\n */\nexport type PlaceDetailsTemplate = ServiceTemplate<\n PlaceDetailsParams,\n GetObject,\n PlaceDetailsResponseAPI,\n PlaceDetailsResponse\n>;\n\n/**\n * Place details template main implementation.\n * @ignore\n */\nexport const placeDetailsTemplate: PlaceDetailsTemplate = {\n requestValidation: { schema: placeDetailsRequestSchema },\n buildRequest: buildPlaceDetailsRequest,\n sendRequest: get,\n parseResponse: parsePlaceDetailsResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport { placeDetailsTemplate } from './placeDetailsTemplate';\nimport type { PlaceDetailsParams, PlaceDetailsResponse } from './types';\n\n/**\n * Retrieve detailed information about a place using its unique identifier.\n *\n * The place details service fetches comprehensive data for a specific place when you\n * have its ID from a previous search or from a place's dataSources. This is useful\n * for getting additional details or refreshing information about a known location.\n *\n * @remarks\n * Use cases:\n * - **Fetch POI details**: Get extended information like reviews, photos, amenities\n * - **Refresh place data**: Update information for a cached place\n * - **Deep linking**: Allow users to share/bookmark specific places\n * - **Related POI navigation**: Explore parent/child relationships\n *\n * The ID can be obtained from:\n * - Previous search results (place.id)\n * - POI details data source (place.properties.dataSources.poiDetails.id)\n * - Related POIs (place.properties.relatedPois[].id)\n * - Deep links or bookmarks\n *\n * @param params - Place details parameters with the place identifier\n *\n * @returns Promise resolving to detailed place information\n *\n * @example\n * ```typescript\n * // Get a search result's full details\n * const searchResult = await discoverPlaces({ query: 'Eiffel Tower' });\n * const placeId = searchResult.features[0].id;\n *\n * const placeDetails = await getPlaceDetails({\n * key: 'your-api-key',\n * entityId: placeId\n * });\n *\n * // Get extended POI details\n * const place = searchResult.features[0];\n * const poiDetailsId = place.properties.dataSources?.poiDetails?.id;\n *\n * if (poiDetailsId) {\n * const detailedPOI = await getPlaceDetails({\n * key: 'your-api-key',\n * entityId: poiDetailsId\n * });\n * // May include additional photos, reviews, extended hours, etc.\n * }\n *\n * // Navigate to related POI\n * const relatedPOI = place.properties.relatedPois?.[0];\n * if (relatedPOI) {\n * const parentPlace = await getPlaceDetails({\n * key: 'your-api-key',\n * entityId: relatedPOI.id\n * });\n * console.log('Parent location:', parentPlace.properties.address);\n * }\n * ```\n *\n * @see [Place by ID API Documentation](https://docs.tomtom.com/search-api/documentation/place-by-id-service/place-by-id)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n *\n * @group Places Search\n */\nexport const getPlaceDetails = async (params: PlaceDetailsParams): Promise<PlaceDetailsResponse> =>\n callService(params, placeDetailsTemplate, 'PlaceDetails');\n","import type { Language } from '@tomtom-org/maps-sdk/core';\nimport type { POICategoryResult } from './types';\n\nconst cache = new Map<Language, POICategoryResult[]>();\nconst textCache = new Map<Language, Record<string, POICategoryResult>>();\n\nconst DEFAULT_LANGUAGE: Language = 'en-GB';\n\nconst getWithDefault = (language: Language | undefined): Language => language ?? DEFAULT_LANGUAGE;\n\nexport const normalizeText = (text: string): string => text.toLowerCase().replaceAll(/[\\s\\-_,]+/g, '');\n\n/** @ignore */\nexport const getCachedCategories = (language: Language | undefined): POICategoryResult[] | undefined => {\n return cache.get(getWithDefault(language));\n};\n\n/** @ignore */\nexport const setCachedCategories = (language: Language | undefined, categories: POICategoryResult[]): void => {\n const lang = getWithDefault(language);\n cache.set(lang, categories);\n textCache.set(lang, buildTextCache(categories));\n};\n\n/** @ignore */\nexport const buildTextCache = (categories: POICategoryResult[]): Record<string, POICategoryResult> => {\n const cache: Record<string, POICategoryResult> = {};\n for (const category of categories) {\n const texts = [category.name, ...category.synonyms].map(normalizeText);\n for (const text of texts) {\n cache[text] = category;\n }\n }\n return cache;\n};\n\n/** @ignore */\nexport const getCachedTextEntries = (language: Language | undefined): Record<string, POICategoryResult> =>\n textCache.get(language ?? DEFAULT_LANGUAGE) ?? {};\n","import { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\n\n/**\n * @ignore\n */\nexport const poiCategoriesRequestSchema = commonServiceRequestSchema.extend({\n filters: z.array(z.string()).optional(),\n});\n","import type { GetObject } from '../shared';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { appendPlacesLanguageParam, buildPlacesRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { POICategoriesParams } from './types';\n\n/**\n * Builds the POI categories request URL from the merged params.\n * The `filters` param is intentionally omitted — it is applied client-side.\n * @ignore\n */\nexport const buildPoiCategoriesRequest = (params: POICategoriesParams): GetObject => {\n const baseUrl = resolvePlacesEndpointUrl(params, 'poiCategories.json');\n const url = new URL(baseUrl);\n appendPlacesLanguageParam(url.searchParams, params);\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import { type POICategory, poiIDsToCategories } from '@tomtom-org/maps-sdk/core';\nimport type { POICategoriesResponse, POICategoryResult, PoiCategoriesResponseAPI } from './types';\n\nconst parsePoiCategory = (api: PoiCategoriesResponseAPI['poiCategories'][number]): POICategoryResult | null => {\n const code = poiIDsToCategories[api.id];\n if (!code) {\n console.error(\n `Received unknown POI category ID ${api.id} (${api.name}). This category will be skipped and unavailable for filtering.`,\n );\n return null;\n }\n return {\n code,\n name: api.name,\n childCategoryCodes: api.childCategoryIds\n .map((id) => poiIDsToCategories[id])\n .filter((c): c is POICategory => !!c),\n synonyms: api.synonyms,\n };\n};\n\n/**\n * @ignore\n */\nexport const parsePoiCategoriesResponse = (apiResponse: PoiCategoriesResponseAPI): POICategoriesResponse => ({\n poiCategories: apiResponse.poiCategories.map(parsePoiCategory).filter((c) => c !== null),\n});\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { poiCategoriesRequestSchema } from './poiCategoriesRequestSchema';\nimport { buildPoiCategoriesRequest } from './requestBuilder';\nimport { parsePoiCategoriesResponse } from './responseParser';\nimport type { POICategoriesParams, POICategoriesResponse, PoiCategoriesResponseAPI } from './types';\n\n/**\n * @ignore\n */\nexport type PoiCategoriesTemplate = ServiceTemplate<\n POICategoriesParams,\n GetObject,\n PoiCategoriesResponseAPI,\n POICategoriesResponse\n>;\n\n/**\n * @ignore\n */\nexport const poiCategoriesTemplate: PoiCategoriesTemplate = {\n requestValidation: { schema: poiCategoriesRequestSchema },\n buildRequest: buildPoiCategoriesRequest,\n apiVersion: 1,\n sendRequest: get,\n parseResponse: parsePoiCategoriesResponse,\n};\n","import type { Language, POICategory } from '@tomtom-org/maps-sdk/core';\nimport { mergeFromGlobal } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { getCachedCategories, getCachedTextEntries, normalizeText, setCachedCategories } from './cache';\nimport { poiCategoriesTemplate } from './poiCategoriesTemplate';\nimport type { POICategoriesParams, POICategoriesResponse, POICategoryResult } from './types';\n\n/**\n * Filters cached categories for `language` against `filters` keyword strings.\n * Each filter is normalized and matched as a substring of category names and synonyms.\n * Results are merged, deduplicated by code, and pruned of child categories whose parent also matched.\n *\n * @param language - Language key for the cache lookup.\n * @param filters - Keyword strings to match against names and synonyms.\n */\nconst filterCategories = (language: Language | undefined, filters: string[]): POICategoryResult[] => {\n const cachedTextEntries = getCachedTextEntries(language);\n const resultsByCode = new Map<POICategory, POICategoryResult>();\n for (const filterTerm of filters) {\n const normalizedFilterInput = normalizeText(filterTerm);\n for (const [text, category] of Object.entries(cachedTextEntries)) {\n // if the category text includes the filter input, add the category to the results:\n if (text.includes(normalizedFilterInput) && category) {\n resultsByCode.set(category.code, category);\n }\n }\n }\n\n // Remove child categories whose parent also matched — the parent already implies them.\n for (const category of resultsByCode.values()) {\n for (const childCode of category.childCategoryCodes) {\n resultsByCode.delete(childCode);\n }\n }\n return [...resultsByCode.values()];\n};\n\n/**\n * Retrieve the list of POI category types supported by the TomTom Search API.\n *\n * The returned `code` values (`POICategory`) serve two purposes:\n * - Pass to `discoverPlaces({ filters: { poiCategories } })` to filter search results by category.\n * - Pass to `POIsModule.filterCategories()` to filter the map's built-in POI icons.\n * Generic parent codes (e.g. `'RESTAURANT'`) are supported there; overly specific child\n * codes (e.g. `'ITALIAN_RESTAURANT'`) are not — use a parent or a `POICategoryGroup` instead.\n *\n * Results are cached in memory per language. The first call fetches from the API;\n * all subsequent calls — including filtered ones — are served from the cache.\n *\n * @param params - Optional parameters. Uses global `TomTomConfig` by default.\n *\n * @example\n * ```typescript\n * // All categories\n * const { poiCategories: all } = await getPOICategories();\n *\n * // Filtered by keyword, merged and deduplicated\n * const { poiCategories: gyms } = await getPOICategories({ filters: ['gym'] });\n * gyms.forEach(c => console.log(c.code, c.name, c.childCategoryCodes));\n * ```\n *\n * @group POI Categories\n */\nexport const getPOICategories = async (params: POICategoriesParams = {}): Promise<POICategoriesResponse> => {\n const { language } = mergeFromGlobal(params);\n\n let allCategories = getCachedCategories(language);\n\n if (!allCategories) {\n const response = await callService(params, poiCategoriesTemplate, 'POICategories');\n allCategories = response.poiCategories;\n setCachedCategories(language, allCategories);\n }\n\n if (params.filters?.length) {\n return { poiCategories: filterCategories(language, params.filters) };\n }\n\n return { poiCategories: allCategories };\n};\n\n/**\n * Convenience wrapper around {@link getPOICategories} that returns a flat array of `POICategory` codes.\n *\n * The returned codes serve two purposes:\n * - Pass to `discoverPlaces({ filters: { poiCategories } })` to filter search results by category.\n * - Pass to `POIsModule.filterCategories()` to filter the map's built-in POI icons.\n * Generic parent codes (e.g. `'RESTAURANT'`) are supported there; overly specific child\n * codes (e.g. `'ITALIAN_RESTAURANT'`) are not — use a parent or a `POICategoryGroup` instead.\n *\n * Accepts the same parameters as {@link getPOICategories} — use `filters` to narrow by keyword.\n *\n * @param params - Optional parameters. Uses global `TomTomConfig` by default.\n *\n * @example\n * ```typescript\n * // All category codes\n * const codes = await getPOICategoryCodes();\n *\n * // Codes matching 'restaurant' — for search or generic map POI filtering\n * const restaurantCodes = await getPOICategoryCodes({ filters: ['restaurant'] });\n * ```\n *\n * @group POI Categories\n */\nexport const getPOICategoryCodes = async (params: POICategoriesParams = {}): Promise<POICategory[]> => {\n const { poiCategories: categories } = await getPOICategories(params);\n return categories.map((category) => category.code);\n};\n","import type { ParseResponseError } from '../shared';\nimport { SDKServiceError } from '../shared';\nimport type { RoutingAPIResponseError } from '../shared/types/apiResponseErrorTypes';\n\n/**\n * @ignore\n * @param apiError\n * @param serviceName\n */\nexport const parseRoutingResponseError: ParseResponseError<RoutingAPIResponseError> = (apiError, serviceName) => {\n const { data, message, status } = apiError;\n const errorMessage = data?.error?.description ?? data?.detailedError?.message ?? message;\n return new SDKServiceError(errorMessage, serviceName, status);\n};\n","import { isNil } from 'lodash-es';\nimport type {\n CombustionEngineModel,\n ConsumptionModelEfficiency,\n ElectricConsumptionModel,\n ElectricEngineModel,\n SpeedToConsumptionRate,\n} from '../types/vehicleEngineParams';\nimport type { VehicleParameters } from '../types/vehicleParams';\nimport type { ChargingPreferences, ChargingPreferencesKWH, ChargingPreferencesPCT } from '../types/vehiclePreferences';\nimport type { CombustionVehicleState, ElectricVehicleStateKWH, ElectricVehicleStatePCT } from '../types/vehicleState';\n\n/**\n * Converts a percentage of a battery's capacity to the kWh figure every charge parameter takes.\n * @ignore\n */\nexport const kWhFromPercentage = (maxChargeKWH: number, percentage: number): number =>\n (maxChargeKWH * percentage) / 100;\n\n/**\n * The consumption, charge and charging-preference query parameters, keyed by the name the API takes\n * them under. Every routing endpoint spells these identically.\n *\n * @ignore\n */\nexport type EnergyQueryParams = Record<string, string>;\n\n/**\n * Whether the caller described an electric vehicle.\n *\n * @remarks\n * `VehicleParameters` is only partly discriminated — the generic member carries no `engineType` —\n * so the tag has to be tested for presence before it can be compared.\n * @ignore\n */\nexport const isElectricVehicle = (\n vehicle: VehicleParameters,\n): vehicle is Extract<VehicleParameters, { engineType: 'electric' }> =>\n 'engineType' in vehicle && vehicle.engineType === 'electric';\n\n/**\n * The engine of an electric vehicle the caller spelled out. A vehicle named by `model.variantId` has\n * none: the service holds its consumption and battery model.\n * @ignore\n */\nexport const electricEngineOf = (vehicle: VehicleParameters): ElectricEngineModel | undefined => {\n if (!isElectricVehicle(vehicle) || !vehicle.model || 'variantId' in vehicle.model) return undefined;\n\n return vehicle.model.engine;\n};\n\n/** The engine of a combustion vehicle, which is always spelled out. @ignore */\nexport const combustionEngineOf = (vehicle: VehicleParameters): CombustionEngineModel | undefined =>\n 'engineType' in vehicle && vehicle.engineType === 'combustion' ? vehicle.model?.engine : undefined;\n\nconst withParameter = (parameters: EnergyQueryParams, name: string, value: number | string | undefined): void => {\n if (!isNil(value)) parameters[name] = String(value);\n};\n\n// e.g. 50,6.3:130,11.5\nconst speedToConsumptionString = (rates: SpeedToConsumptionRate[]): string =>\n rates.map((rate) => `${rate.speedKMH},${rate.consumptionUnitsPer100KM}`).join(':');\n\n/** Efficiency parameters, which are spelled the same for either engine type. @ignore */\nexport const efficiencyParams = (efficiency?: ConsumptionModelEfficiency): EnergyQueryParams => {\n const parameters: EnergyQueryParams = {};\n if (!efficiency) return parameters;\n\n withParameter(parameters, 'accelerationEfficiency', efficiency.acceleration);\n withParameter(parameters, 'decelerationEfficiency', efficiency.deceleration);\n withParameter(parameters, 'uphillEfficiency', efficiency.uphill);\n withParameter(parameters, 'downhillEfficiency', efficiency.downhill);\n\n return parameters;\n};\n\n/** @ignore */\nexport const electricConsumptionParams = (consumption: ElectricConsumptionModel): EnergyQueryParams => {\n const parameters: EnergyQueryParams = {};\n if (consumption.speedsToConsumptionsKWH?.length)\n parameters.constantSpeedConsumptionInkWhPerHundredkm = speedToConsumptionString(\n consumption.speedsToConsumptionsKWH,\n );\n\n withParameter(parameters, 'auxiliaryPowerInkW', consumption.auxiliaryPowerInkW);\n withParameter(parameters, 'consumptionInkWhPerkmAltitudeGain', consumption.consumptionInKWHPerKMAltitudeGain);\n withParameter(parameters, 'recuperationInkWhPerkmAltitudeLoss', consumption.recuperationInKWHPerKMAltitudeLoss);\n\n return { ...parameters, ...efficiencyParams(consumption.efficiency) };\n};\n\n/** @ignore */\nexport const combustionConsumptionParams = (engine: CombustionEngineModel): EnergyQueryParams => {\n const consumption = engine.consumption;\n const parameters: EnergyQueryParams = {};\n if (consumption.speedsToConsumptionsLiters?.length)\n parameters.constantSpeedConsumptionInLitersPerHundredkm = speedToConsumptionString(\n consumption.speedsToConsumptionsLiters,\n );\n\n withParameter(parameters, 'auxiliaryPowerInLitersPerHour', consumption.auxiliaryPowerInLitersPerHour);\n withParameter(parameters, 'fuelEnergyDensityInMJoulesPerLiter', consumption.fuelEnergyDensityInMJoulesPerLiter);\n\n return { ...parameters, ...efficiencyParams(consumption.efficiency) };\n};\n\n/**\n * The battery capacity declared on an explicit electric model, which every percentage-based charge\n * and charging preference is converted against.\n *\n * @ignore\n */\nexport const maxChargeKWHOf = (vehicle: VehicleParameters): number | undefined =>\n electricEngineOf(vehicle)?.charging?.maxChargeKWH;\n\n/** @ignore */\nexport const maxChargeParams = (engine: ElectricEngineModel): EnergyQueryParams => {\n const parameters: EnergyQueryParams = {};\n if (engine.charging?.maxChargeKWH) parameters.maxChargeInkWh = String(engine.charging.maxChargeKWH);\n\n return parameters;\n};\n\n/** @ignore */\nexport const electricEngineParams = (engine: ElectricEngineModel): EnergyQueryParams => ({\n ...electricConsumptionParams(engine.consumption),\n ...maxChargeParams(engine),\n});\n\n/**\n * The charge a vehicle sets off with, always as kWh: a percentage is only meaningful once converted\n * against the battery capacity, so a percentage without one yields nothing.\n *\n * @remarks\n * A flat battery is a charge the API takes, and a different request from one that says nothing about\n * the charge — hence the nil tests rather than truthiness.\n * @ignore\n */\nexport const currentChargeKWHOf = (vehicle: VehicleParameters): number | undefined => {\n const absoluteCharge = (vehicle.state as ElectricVehicleStateKWH | undefined)?.currentChargeInkWh;\n if (!isNil(absoluteCharge)) return absoluteCharge;\n\n const percentage = (vehicle.state as ElectricVehicleStatePCT | undefined)?.currentChargePCT;\n const maxChargeKWH = maxChargeKWHOf(vehicle);\n if (!isNil(percentage) && maxChargeKWH) return kWhFromPercentage(maxChargeKWH, percentage);\n\n return undefined;\n};\n\n/** {@link currentChargeKWHOf} as the query parameter the API takes it under. @ignore */\nexport const currentChargeParams = (vehicle: VehicleParameters): EnergyQueryParams => {\n const parameters: EnergyQueryParams = {};\n withParameter(parameters, 'currentChargeInkWh', currentChargeKWHOf(vehicle));\n\n return parameters;\n};\n\n/** @ignore */\nexport const currentFuelParams = (vehicle: VehicleParameters): EnergyQueryParams => {\n const parameters: EnergyQueryParams = {};\n withParameter(\n parameters,\n 'currentFuelInLiters',\n (vehicle.state as CombustionVehicleState | undefined)?.currentFuelInLiters,\n );\n\n return parameters;\n};\n\n/**\n * The charging stops a caller asked for, which only an electric vehicle can declare.\n *\n * @remarks\n * Charging preferences are what select the charging-stops endpoint, so the request builder asks\n * this too — reaching them one way keeps the endpoint it picks and the parameters it sends from\n * disagreeing.\n * @ignore\n */\nexport const chargingPreferencesOf = (vehicle: VehicleParameters): ChargingPreferences | undefined =>\n isElectricVehicle(vehicle) ? vehicle.preferences?.chargingPreferences : undefined;\n\n/**\n * The minimum charge to keep at stops and on arrival, always as kWh. Absolute values win; percentages\n * need the battery capacity to convert against, and yield nothing without one.\n *\n * @ignore\n */\nexport const chargingPreferenceParams = (vehicle: VehicleParameters): EnergyQueryParams => {\n const preferences = chargingPreferencesOf(vehicle);\n const parameters: EnergyQueryParams = {};\n if (!preferences) return parameters;\n\n const absolutePreferences = preferences as ChargingPreferencesKWH;\n if (\n !isNil(absolutePreferences.minChargeAtDestinationInkWh) ||\n !isNil(absolutePreferences.minChargeAtChargingStopsInkWh)\n ) {\n withParameter(parameters, 'minChargeAtDestinationInkWh', absolutePreferences.minChargeAtDestinationInkWh);\n withParameter(parameters, 'minChargeAtChargingStopsInkWh', absolutePreferences.minChargeAtChargingStopsInkWh);\n\n return parameters;\n }\n\n const maxChargeKWH = maxChargeKWHOf(vehicle);\n if (!maxChargeKWH) return parameters;\n\n const percentagePreferences = preferences as ChargingPreferencesPCT;\n if (!isNil(percentagePreferences.minChargeAtDestinationPCT))\n parameters.minChargeAtDestinationInkWh = String(\n kWhFromPercentage(maxChargeKWH, percentagePreferences.minChargeAtDestinationPCT),\n );\n\n if (!isNil(percentagePreferences.minChargeAtChargingStopsPCT))\n parameters.minChargeAtChargingStopsInkWh = String(\n kWhFromPercentage(maxChargeKWH, percentagePreferences.minChargeAtChargingStopsPCT),\n );\n\n return parameters;\n};\n\n/** @ignore */\nexport const setEnergyParams = (urlParams: URLSearchParams, parameters: EnergyQueryParams): void => {\n for (const [name, value] of Object.entries(parameters)) urlParams.set(name, value);\n};\n","import { currentTypes, plugTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { tollTransponderOptions } from '../types/vehicleRestrictionParams';\n\n// Common validation schemas\nconst positiveNumber = z.number().positive();\nconst nonNegativeNumber = z.number().min(0);\nconst percentageNumber = z.number().min(0).max(100);\n\n// Common optional schemas\nconst optionalPositiveNumber = positiveNumber.optional();\nconst optionalNonNegativeNumber = nonNegativeNumber.optional();\nconst optionalNormalizedNumber = z.number().min(0).max(1).optional();\n\n// Speed to consumption rate schema\nconst speedToConsumptionRateSchema = z.object({\n speedKMH: z.number(),\n consumptionUnitsPer100KM: z.number(),\n});\n\n// Efficiency schema for consumption models\nconst efficiencySchema = z\n .object({\n acceleration: optionalNormalizedNumber,\n deceleration: optionalNormalizedNumber,\n uphill: optionalNormalizedNumber,\n downhill: optionalNormalizedNumber,\n })\n .optional();\n\n// Base consumption model schema\nconst baseConsumptionModelSchema = {\n efficiency: efficiencySchema,\n};\n\n// Consumption array validation\nconst speedConsumptionArray = z.array(speedToConsumptionRateSchema).min(1).max(25);\n\n// Combustion consumption model schema\nconst combustionConsumptionModelSchema = z.object({\n ...baseConsumptionModelSchema,\n speedsToConsumptionsLiters: speedConsumptionArray,\n auxiliaryPowerInLitersPerHour: optionalNonNegativeNumber,\n fuelEnergyDensityInMJoulesPerLiter: z.number().min(1).optional(),\n});\n\n// Electric consumption model schema\nconst electricConsumptionModelSchema = z.object({\n ...baseConsumptionModelSchema,\n speedsToConsumptionsKWH: speedConsumptionArray,\n auxiliaryPowerInkW: optionalNonNegativeNumber,\n consumptionInKWHPerKMAltitudeGain: z.number().max(500).optional(),\n recuperationInKWHPerKMAltitudeLoss: optionalNonNegativeNumber,\n});\n\n// Charging connector schema\nconst chargingConnectorSchema = z.object({\n currentType: z.enum(currentTypes),\n plugTypes: z.array(z.enum(plugTypes)).min(1),\n efficiency: optionalNormalizedNumber,\n baseLoadInkW: optionalNonNegativeNumber,\n maxPowerInkW: optionalNonNegativeNumber,\n maxVoltageInV: optionalNonNegativeNumber,\n maxCurrentInA: optionalNonNegativeNumber,\n voltageRange: z\n .object({\n minVoltageInV: optionalNonNegativeNumber,\n maxVoltageInV: z.number().optional(),\n })\n .optional(),\n});\n\n// Battery curve schema\nconst batteryCurveSchema = z.object({\n stateOfChargeInkWh: nonNegativeNumber,\n maxPowerInkW: positiveNumber,\n});\n\n// Charging model schema\nconst chargingModelSchema = z.object({\n maxChargeKWH: positiveNumber,\n batteryCurve: z.array(batteryCurveSchema).max(20).optional(),\n chargingConnectors: z.array(chargingConnectorSchema).min(1).optional(),\n chargingTimeOffsetInSec: optionalNonNegativeNumber,\n});\n\n// Engine model schemas\nconst combustionEngineModelSchema = z.object({\n consumption: combustionConsumptionModelSchema,\n});\nconst electricEngineModelSchema = z.object({\n consumption: electricConsumptionModelSchema,\n charging: chargingModelSchema.optional(),\n});\n\n// Vehicle dimensions schema\nconst vehicleDimensionsSchema = z\n .object({\n weightKG: optionalPositiveNumber,\n })\n .optional();\n\n// Vehicle model schemas\nconst predefinedVehicleModelSchema = z.object({\n variantId: z.string(),\n});\nconst explicitVehicleModelSchema = z.object({\n dimensions: vehicleDimensionsSchema,\n engine: z.union([combustionEngineModelSchema, electricEngineModelSchema]).optional(),\n});\nconst vehicleModelSchema = z.union([predefinedVehicleModelSchema, explicitVehicleModelSchema]).optional();\n\n// Vehicle state schemas\nconst genericVehicleStateSchema = z.object({\n heading: z.number().min(0).max(360).optional(),\n});\n\nconst combustionVehicleStateSchema = genericVehicleStateSchema.extend({\n currentFuelInLiters: nonNegativeNumber,\n});\n\nconst electricVehicleStateByPercentageSchema = genericVehicleStateSchema.extend({\n currentChargePCT: percentageNumber,\n});\n\nconst electricVehicleStateByKwhSchema = genericVehicleStateSchema.extend({\n currentChargeInkWh: nonNegativeNumber,\n});\n\nconst electricVehicleStateSchema = z.union([electricVehicleStateByPercentageSchema, electricVehicleStateByKwhSchema]);\n\n// Charging preferences schemas\nconst chargingPreferencesPCTSchema = z.object({\n minChargeAtDestinationPCT: percentageNumber,\n minChargeAtChargingStopsPCT: z.number().min(0).max(50),\n});\n\nconst chargingPreferencesKWHSchema = z.object({\n minChargeAtDestinationInkWh: nonNegativeNumber,\n minChargeAtChargingStopsInkWh: nonNegativeNumber,\n});\n\nconst chargingPreferencesSchema = z.union([chargingPreferencesPCTSchema, chargingPreferencesKWHSchema]);\nconst electricVehiclePreferencesSchema = z.object({\n chargingPreferences: chargingPreferencesSchema.optional(),\n});\n\n// Vehicle restrictions schema\nconst vehicleRestrictionsSchema = z\n .object({\n maxSpeedKMH: z.number().min(0).max(250).optional(),\n tollTransponder: z.enum(tollTransponderOptions).optional(),\n })\n .optional();\n\n// Base vehicle parameters schema\nconst baseVehicleParamsSchema = {\n model: vehicleModelSchema,\n restrictions: vehicleRestrictionsSchema,\n};\n\n// Vehicle parameters schemas\nconst genericVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.undefined().optional(),\n state: genericVehicleStateSchema.optional(),\n});\n\nconst combustionVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.literal('combustion'),\n state: combustionVehicleStateSchema.optional(),\n});\n\nconst electricVehicleParamsSchema = z.object({\n ...baseVehicleParamsSchema,\n engineType: z.literal('electric'),\n state: electricVehicleStateSchema.optional(),\n preferences: electricVehiclePreferencesSchema.optional(),\n});\n\n/**\n * @ignore\n */\nexport const vehicleParametersSchema = z.union([\n z.discriminatedUnion('engineType', [combustionVehicleParamsSchema, electricVehicleParamsSchema]),\n genericVehicleParamsSchema,\n]);\n","import { avoidableTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { routeTypes } from '../types/commonRoutingParams';\nimport { hasBBoxSchema } from './geometriesSchema';\nimport { vehicleParametersSchema } from './vehicleParamsSchema';\n\n/**\n * @ignore\n */\nexport const costModelSchema = z.object({\n avoid: z.array(z.enum(avoidableTypes)).optional(),\n traffic: z.enum(['live', 'historical']).optional(),\n routeType: z.enum(routeTypes).optional(),\n avoidAreas: z.array(hasBBoxSchema).max(10).optional(),\n});\n\n/**\n * @ignore\n */\nexport const departArriveSchema = z.object({\n option: z.enum(['departAt', 'arriveBy']),\n date: z.date(),\n});\n\n/**\n * @ignore\n */\nexport const commonRoutingRequestSchema = z.object({\n costModel: costModelSchema.optional(),\n travelMode: z.string().optional(),\n vehicle: vehicleParametersSchema.optional(),\n when: departArriveSchema.optional(),\n});\n","import type { Avoidable, HasLngLat } from '@tomtom-org/maps-sdk/core';\nimport type {\n CombustionVehicleParams,\n CombustionVehicleState,\n CommonRoutingParams,\n CommonServiceParams,\n CostModel,\n DepartArriveParams,\n ElectricVehicleParams,\n ElectricVehicleState,\n GenericVehicleParams,\n VehicleParameters,\n VehicleRestrictions,\n} from '../../shared';\nimport type { ReachableRangeRequestAPI } from './apiRequestTypes';\nimport type { ReachableRangeResponseAPI } from './apiResponseTypes';\n\nexport const budgetTypes = [\n 'timeMinutes',\n 'remainingChargePCT',\n 'spentChargePCT',\n 'spentFuelLiters',\n 'distanceKM',\n] as const;\n\n/**\n * Type of budget constraint for reachable range calculation.\n *\n * @remarks\n * **Budget Types:**\n * - `timeMinutes`: Travel time in minutes (isochrone)\n * - `distanceKM`: Travel distance in kilometers (isodistance)\n * - `remainingChargePCT`: Remaining battery charge in percentage (EV)\n * - `spentChargePCT`: Battery charge consumed in percentage (EV)\n * - `spentFuelLiters`: Fuel consumed in liters (combustion)\n *\n * Both charge budgets are converted against the battery capacity, so they need an electric\n * `vehicle` with `model.engine.charging.maxChargeKWH`; `remainingChargePCT` also needs its current\n * charge in `vehicle.state`.\n *\n * **Common Use Cases:**\n * - Time: \"Where can I reach in 30 minutes?\"\n * - Distance: \"What's within 10 km?\"\n * - EV charge: \"How far can I go on 50% battery?\"\n * - Fuel: \"Range with 20 liters of fuel\"\n *\n * @example\n * ```typescript\n * const timeType: BudgetType = 'timeMinutes';\n * const distanceType: BudgetType = 'distanceKM';\n * const evType: BudgetType = 'remainingChargePCT';\n * ```\n *\n * @group Reachable Range\n */\nexport type BudgetType = (typeof budgetTypes)[number];\n\n/**\n * Budget constraint for reachable range calculation.\n *\n * Defines the limit (time, distance, or fuel/charge) for calculating\n * how far you can travel from a starting point.\n *\n * @remarks\n * **Use Cases:**\n * - Service area visualization (30-min delivery zone)\n * - EV range anxiety mitigation (show reachable area)\n * - Emergency response coverage (10-min response time)\n * - Delivery zone planning\n * - Store location analysis\n *\n * @example\n * ```typescript\n * // 30-minute travel time\n * const timeBudget: ReachableRangeBudget = {\n * type: 'timeMinutes',\n * value: 30\n * };\n *\n * // 50 km distance\n * const distanceBudget: ReachableRangeBudget = {\n * type: 'distanceKM',\n * value: 50\n * };\n *\n * // 50% battery remaining\n * const evBudget: ReachableRangeBudget = {\n * type: 'remainingChargePCT',\n * value: 50\n * };\n *\n * // 20 liters of fuel\n * const fuelBudget: ReachableRangeBudget = {\n * type: 'spentFuelLiters',\n * value: 20\n * };\n * ```\n *\n * @group Reachable Range\n */\nexport type ReachableRangeBudget = {\n /**\n * The type of budget, including units. See {@link BudgetType}.\n */\n type: BudgetType;\n\n /**\n * The value of the budget based on the units mentioned in the type.\n *\n * @remarks\n * **Typical Values:**\n * - Time: 5-60 minutes\n * - Distance: 5-100 km\n * - Charge: 10-100 percentage\n * - Fuel: 5-50 liters\n *\n * @example\n * ```typescript\n * value: 30 // 30 minutes/km/percent/liters depending on type\n * value: 15.5 // Decimal values supported\n * ```\n */\n value: number;\n};\n\n/**\n * Route features `calculateReachableRange` can avoid.\n *\n * @remarks\n * Every {@link Avoidable} except `alreadyUsedRoads`, which this endpoint rejects with\n * *\"Avoid alreadyUsedRoads not supported for calculateReachableRange\"*. It stays available on\n * `calculateRoute`.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeAvoidable = Exclude<Avoidable, 'alreadyUsedRoads'>;\n\n/**\n * Cost model accepted by `calculateReachableRange`.\n *\n * @remarks\n * {@link CostModel} without `avoidAreas`, which this endpoint has no parameter for, and with the\n * narrower {@link ReachableRangeAvoidable} avoid list.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeCostModel = Omit<CostModel, 'avoid' | 'avoidAreas'> & {\n /**\n * Route features to avoid when calculating the range.\n */\n avoid?: ReachableRangeAvoidable[];\n};\n\n/**\n * Vehicle parameters accepted by `calculateReachableRange`.\n *\n * @remarks\n * {@link VehicleParameters} without the two things this endpoint has no parameter for:\n *\n * - `state.heading`, which it rejects with *\"Unknown JSON field '.vehicleHeadingInDegrees'\"*. The\n * routing API takes it under that name.\n * - `preferences`, whose only member is `chargingPreferences`. Those belong to the charging-stops\n * endpoint, and a range has no stops to plan. A predefined `model.variantId` therefore needs\n * nothing beside it here, unlike on `calculateRoute`.\n *\n * Both stay available on `calculateRoute`. Everything else carries over, including the\n * engine-specific `state` variants — an electric vehicle picks between the percentage and kWh\n * forms.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeVehicleParameters = (\n | Omit<GenericVehicleParams, 'state' | 'preferences'>\n | (Omit<CombustionVehicleParams, 'state' | 'preferences'> & { state?: CombustionVehicleState })\n | (Omit<ElectricVehicleParams, 'state' | 'preferences'> & { state?: ElectricVehicleState })\n) &\n VehicleRestrictions;\n\n/**\n * Parameters specific to reachable range calculation.\n *\n * @remarks\n * These parameters are combined with common routing parameters to calculate\n * the reachable area from a starting point.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeOwnParams = {\n /**\n * How the routes underlying the range are optimized, and what they avoid.\n */\n costModel?: ReachableRangeCostModel;\n\n /**\n * Parameters for the vehicle whose reachable range is calculated.\n */\n vehicle?: ReachableRangeVehicleParameters;\n\n /**\n * Location from which the range calculation should start.\n *\n * @remarks\n * The center point from which reachability is calculated. Can be:\n * - An object with `lon` and `lat` properties\n * - An array `[longitude, latitude]`\n *\n * @example\n * ```typescript\n * // Object format\n * origin: { lon: 4.9, lat: 52.3 }\n *\n * // Array format\n * origin: [4.9, 52.3]\n * ```\n */\n origin: HasLngLat;\n\n /**\n * The budget for the reachable range calculation.\n *\n * @remarks\n * Consists of a type indicating whether it's about time, distance, or\n * fuel/charge and the units, plus its value.\n *\n * Determines the extent of the reachable area polygon.\n */\n budget: ReachableRangeBudget;\n\n /**\n * Maximum ferry length in meters to consider.\n *\n * @remarks\n * Ferries longer than this value will be avoided in the calculation.\n * Useful for excluding long ferry routes that might extend the range\n * unrealistically.\n *\n * @example\n * ```typescript\n * maxFerryLengthMeters: 5000 // Avoid ferries longer than 5 km\n * ```\n */\n maxFerryLengthMeters?: number;\n\n /**\n * Applies post-processing to smooth the polygon boundary for better visual appearance.\n *\n * @remarks\n * Stronger smoothing generally results in more points.\n *\n * @default `'strong'`\n *\n * @example\n * ```typescript\n * smoothing: 'strong'\n * ```\n */\n smoothing?: 'none' | 'weak' | 'strong';\n\n /**\n * Specifies when to depart.\n *\n * @remarks\n * If past dates are supplied or dates that are impossible to achieve,\n * it will default to departing now.\n *\n * **Traffic Impact:**\n * - Future departure times use predictive traffic\n * - Current time uses live traffic\n * - Historic times use historical patterns\n *\n * @default Depart now\n *\n * @example\n * ```typescript\n * when: { option: 'departAt', date: new Date('2025-10-20T08:00:00Z') }\n * ```\n */\n when?: DepartArriveParams<'departAt'>;\n};\n\n/**\n * Complete parameters for calculating a reachable range.\n *\n * Combines common service parameters, routing parameters, and reachable range\n * specific options to compute an isochrone or isodistance polygon.\n *\n * @remarks\n * **What it Returns:**\n * A polygon representing the area reachable from the origin within the\n * specified budget (time, distance, or fuel/charge).\n *\n * **Use Cases:**\n * - Delivery zone visualization\n * - Service area mapping\n * - Emergency response coverage\n * - EV range display\n * - Store catchment areas\n * - Real estate search (30-min commute)\n *\n * **Traffic Consideration:**\n * Results vary based on departure time and traffic conditions. Use\n * appropriate `when` values for accurate predictions.\n *\n * @example\n * ```typescript\n * // 30-minute driving range in live traffic\n * const params: ReachableRangeParams = {\n * origin: [4.9, 52.3],\n * budget: { type: 'timeMinutes', value: 30 },\n * costModel: { routeType: 'fast', traffic: 'live' }\n * };\n *\n * // EV range down to 50% remaining battery\n * const evParams: ReachableRangeParams = {\n * origin: [4.9, 52.3],\n * budget: { type: 'remainingChargePCT', value: 50 },\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * engine: {\n * consumption: {\n * speedsToConsumptionsKWH: [{ speedKMH: 80, consumptionUnitsPer100KM: 12 }]\n * },\n * charging: { maxChargeKWH: 100 }\n * }\n * },\n * state: { currentChargePCT: 80 }\n * }\n * };\n *\n * // Avoid toll roads\n * const noTollParams: ReachableRangeParams = {\n * origin: [4.9, 52.3],\n * budget: { type: 'timeMinutes', value: 45 },\n * costModel: { avoid: ['tollRoads'] }\n * };\n * ```\n *\n * @group Reachable Range\n */\nexport type ReachableRangeParams = CommonServiceParams<ReachableRangeRequestAPI, ReachableRangeResponseAPI> &\n Omit<CommonRoutingParams, 'costModel' | 'vehicle'> &\n ReachableRangeOwnParams;\n\n/**\n * The `properties` of a reachable range polygon: the parameters it was calculated with.\n *\n * @remarks\n * {@link ReachableRangeParams} without any {@link CommonServiceParams} — the API key, base URLs,\n * callbacks and abort signal never travel with the result.\n *\n * @group Reachable Range\n */\nexport type ReachableRangeProperties = Omit<ReachableRangeParams, keyof CommonServiceParams>;\n","import { avoidableTypes } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { currentChargeKWHOf, maxChargeKWHOf } from '../shared/request/vehicleEnergyParams';\nimport {\n commonRoutingRequestSchema,\n costModelSchema,\n departArriveSchema,\n} from '../shared/schema/commonRoutingRequestSchema';\nimport { hasLngLatSchema } from '../shared/schema/geometriesSchema';\nimport { vehicleParametersSchema } from '../shared/schema/vehicleParamsSchema';\nimport type { SchemaRefinement } from '../shared/types/validation';\nimport { type BudgetType, budgetTypes, type ReachableRangeParams } from './types/reachableRangeParams';\n\nconst reachableRangeRequestSchemaMandatory = z.object({\n origin: hasLngLatSchema,\n budget: z.object({\n type: z.enum(budgetTypes),\n value: z.number().min(0),\n }),\n});\n\n// Each narrowing below mirrors one the types already make, for callers who are not type-checked:\n// a parameter the endpoint answers `400` for fails at the call instead of being stripped in\n// silence. Each message names what to do instead.\n\nconst reachableRangeAvoidableSchema = z.enum(avoidableTypes).exclude(['alreadyUsedRoads']);\n\n// Matches `ReachableRangeVehicleParameters`, probed 2026-09-01. Charging preferences encode to\n// `minChargeAt*InkWh`, which only the charging-stops endpoint takes — this one plans no stops.\nconst reachableRangeVehicleSchema = vehicleParametersSchema.check(\n z.refine(\n (vehicle) => !(vehicle.state && 'heading' in vehicle.state),\n 'vehicle.state.heading: calculateReachableRange has no vehicle heading parameter. Remove it, or use calculateRoute, which accepts it.',\n ),\n z.refine(\n (vehicle) =>\n !(vehicle.engineType === 'electric' && vehicle.preferences && 'chargingPreferences' in vehicle.preferences),\n 'vehicle.preferences.chargingPreferences: calculateReachableRange plans no charging stops. Remove them, or use calculateRoute, which plans stops for them.',\n ),\n);\n\n// Matches `ReachableRangeParams.when`, which is departure-only.\nconst reachableRangeWhenSchema = departArriveSchema.check(\n z.refine(\n (when) => when.option !== 'arriveBy',\n 'when.arriveBy: calculateReachableRange has no arrival-time parameter. Use when.departAt instead, or omit when entirely.',\n ),\n);\n\n// Matches `ReachableRangeCostModel`. `avoidAreas` accepts anything so that the refinement, not a\n// shape mismatch, is what reports it.\nconst reachableRangeCostModelSchema = costModelSchema\n .extend({\n avoid: z.array(reachableRangeAvoidableSchema).optional(),\n avoidAreas: z.unknown().optional(),\n })\n .check(\n z.refine(\n (costModel) => costModel.avoidAreas === undefined,\n 'costModel.avoidAreas: calculateReachableRange has no avoid-areas parameter. Remove it — the other cost-model options are accepted.',\n ),\n );\n\nconst reachableRangeRequestSchemaOptional = z.object({\n when: reachableRangeWhenSchema.optional(),\n vehicle: reachableRangeVehicleSchema.optional(),\n costModel: reachableRangeCostModelSchema.optional(),\n maxFerryLengthMeters: z.number().min(0).optional(),\n smoothing: z.enum(['none', 'weak', 'strong']).optional(),\n});\n\nconst reachableRangeRequestSchema = commonRoutingRequestSchema.extend(\n reachableRangeRequestSchemaMandatory.extend(reachableRangeRequestSchemaOptional.shape).shape,\n);\n\n// Both charge budgets are a percentage of the battery and reach the wire as `energyBudgetInkWh`, so\n// without a capacity to convert against the request goes out with no budget at all. `spentFuelLiters`\n// needs no conversion and carries no such requirement.\n//\n// Each asks the request builder's own readers rather than re-deciding what is convertible, so what\n// validation accepts and what the builder can encode cannot drift apart.\nconst chargeBudgetTypes = new Set<BudgetType>(['spentChargePCT', 'remainingChargePCT']);\n\nconst batteryCapacityRefinement: SchemaRefinement<ReachableRangeParams> = {\n check: (data: ReachableRangeParams): boolean =>\n !chargeBudgetTypes.has(data.budget.type) || (!!data.vehicle && maxChargeKWHOf(data.vehicle) !== undefined),\n message:\n \"budget: a charge budget is a percentage of the battery, so it needs a capacity to convert against. Set vehicle.engineType to 'electric' and vehicle.model.engine.charging.maxChargeKWH, or budget by time or distance instead.\",\n};\n\nconst currentChargeRefinement: SchemaRefinement<ReachableRangeParams> = {\n check: (data: ReachableRangeParams): boolean =>\n data.budget.type !== 'remainingChargePCT' || (!!data.vehicle && currentChargeKWHOf(data.vehicle) !== undefined),\n message:\n 'budget.remainingChargePCT: a range down to a remaining charge is measured from the charge the vehicle sets off with. Set vehicle.state.currentChargeInkWh or vehicle.state.currentChargePCT, or use spentChargePCT to budget a share of the battery instead.',\n};\n\n/**\n * @ignore\n */\nexport const reachableRangeRequestValidationConfig = {\n schema: reachableRangeRequestSchema,\n refinements: [batteryCapacityRefinement, currentChargeRefinement],\n};\n","import type { CommonRoutingBodyAPI, CommonVehicleBodyAPI } from '../types/apiRoutingRequestTypes';\nimport type { CommonRoutingParams, CostModel } from '../types/commonRoutingParams';\nimport type { VehicleParameters } from '../types/vehicleParams';\nimport {\n combustionConsumptionParams,\n combustionEngineOf,\n currentChargeParams,\n currentFuelParams,\n electricEngineOf,\n electricEngineParams,\n isElectricVehicle,\n setEnergyParams,\n} from './vehicleEnergyParams';\n\n// Typed on what this builder reads rather than on one endpoint's params type, so the\n// reachable-range params — whose `when` is departure-only and whose cost model has no\n// `avoidAreas` — satisfy it without a cast.\ntype CommonRoutingBodyInput = Pick<CommonRoutingParams, 'travelMode' | 'when'> & {\n costModel?: Omit<CostModel, 'avoidAreas'>;\n};\n\n/**\n * Builds the body fields shared by every routing endpoint.\n * @ignore\n */\nexport const buildCommonRoutingBody = (params: CommonRoutingBodyInput): CommonRoutingBodyAPI => ({\n ...(params.costModel?.routeType && { routeType: params.costModel.routeType }),\n ...(params.costModel?.traffic && { traffic: params.costModel.traffic }),\n ...(params.costModel?.avoid?.length && { avoids: params.costModel.avoid }),\n ...(params.travelMode && { travelMode: params.travelMode }),\n ...(params.when?.option === 'departAt' &&\n params.when.date && { departureDateTime: params.when.date.toISOString() }),\n});\n\n/**\n * The `variantId` of a vehicle named from the Vehicle Database rather than spelled out.\n * @ignore\n */\nexport const predefinedModelIdOf = (vehicle: VehicleParameters): string | undefined =>\n vehicle.model && 'variantId' in vehicle.model ? vehicle.model.variantId : undefined;\n\n/**\n * Builds the vehicle body fields shared by every routing endpoint.\n * @ignore\n */\nexport const buildCommonVehicleBody = (vehicle: VehicleParameters): CommonVehicleBodyAPI => {\n const body: CommonVehicleBodyAPI = {};\n if (isElectricVehicle(vehicle)) body.vehicleEngineType = 'electric';\n\n if (vehicle.model && !('variantId' in vehicle.model) && vehicle.model.dimensions?.weightKG)\n body.vehicleWeightInKilograms = vehicle.model.dimensions.weightKG;\n\n if (vehicle.restrictions?.maxSpeedKMH) body.vehicleMaxSpeedInKilometersPerHour = vehicle.restrictions.maxSpeedKMH;\n\n if (vehicle.restrictions?.tollTransponder)\n body.vehicleHasElectronicTollCollectionTransponder = vehicle.restrictions.tollTransponder;\n\n return body;\n};\n\n/**\n * Appends the consumption model and the energy the vehicle sets off with, as query parameters.\n *\n * @remarks\n * Every routing endpoint takes both in the query rather than the body, and under the same names, so\n * all of them append them this way. The two are independent: a vehicle named by `model.variantId`\n * carries no consumption model of its own — the service holds it — and still reports its charge or\n * fuel level.\n * @ignore\n */\nexport const appendVehicleEnergyParams = (url: URL, vehicle: VehicleParameters): void => {\n const electricEngine = electricEngineOf(vehicle);\n const combustionEngine = combustionEngineOf(vehicle);\n setEnergyParams(\n url.searchParams,\n isElectricVehicle(vehicle)\n ? { ...(electricEngine && electricEngineParams(electricEngine)), ...currentChargeParams(vehicle) }\n : { ...(combustionEngine && combustionConsumptionParams(combustionEngine)), ...currentFuelParams(vehicle) },\n );\n};\n\n/**\n * {@link appendVehicleEnergyParams}, plus the `vehicleModelId` of a predefined model. Only\n * `/calculateLongDistanceEVRoute` and `/calculateReachableRange` take the latter; `/routes/calculate`\n * answers `400` for it.\n * @ignore\n */\nexport const appendVehicleQueryParams = (url: URL, vehicle: VehicleParameters): void => {\n const variantId = predefinedModelIdOf(vehicle);\n if (variantId) url.searchParams.set('vehicleModelId', variantId);\n\n appendVehicleEnergyParams(url, vehicle);\n};\n","import { getPositionStrict, type TomTomAPIHeaders } from '@tomtom-org/maps-sdk/core';\nimport { appendOptionalParam, buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport {\n appendVehicleQueryParams,\n buildCommonRoutingBody,\n buildCommonVehicleBody,\n} from '../shared/request/routingBodyBuilder';\nimport { currentChargeKWHOf, kWhFromPercentage, maxChargeKWHOf } from '../shared/request/vehicleEnergyParams';\nimport type { ReachableRangePOSTDataAPI, ReachableRangeRequestAPI } from './types/apiRequestTypes';\nimport type { ReachableRangeParams } from './types/reachableRangeParams';\n\n// The one response field the parser reads. Naming it rather than asking for the whole\n// `reachableRange` object keeps a field added to the endpoint later off the wire.\nconst ATTRIBUTES = 'reachableRange(boundary)';\n\nconst buildUrl = (params: ReachableRangeParams): URL =>\n new URL(`${params.commonBaseURL}/maps/orbis/routing/calculateReachableRange`);\n\n// The endpoint requires exactly one budget, as a query parameter on the POST.\nconst appendBudget = (urlParams: URLSearchParams, params: ReachableRangeParams): void => {\n const budget = params.budget;\n const maxChargeKWH = params.vehicle && maxChargeKWHOf(params.vehicle);\n switch (budget.type) {\n case 'timeMinutes':\n urlParams.append('timeBudgetInSec', (budget.value * 60).toString());\n break;\n case 'distanceKM':\n urlParams.append('distanceBudgetInMeters', (budget.value * 1000).toString());\n break;\n case 'spentFuelLiters':\n urlParams.append('fuelBudgetInLiters', budget.value.toString());\n break;\n case 'spentChargePCT':\n if (maxChargeKWH)\n urlParams.append('energyBudgetInkWh', kWhFromPercentage(maxChargeKWH, budget.value).toString());\n\n break;\n case 'remainingChargePCT': {\n const currentChargeKWH = params.vehicle && currentChargeKWHOf(params.vehicle);\n if (maxChargeKWH && currentChargeKWH !== undefined) {\n const remainingKWH = kWhFromPercentage(maxChargeKWH, budget.value);\n urlParams.append('energyBudgetInkWh', Math.max(0, currentChargeKWH - remainingKWH).toString());\n }\n break;\n }\n }\n};\n\nconst buildPostData = (params: ReachableRangeParams): ReachableRangePOSTDataAPI => ({\n origin: { type: 'Point', coordinates: getPositionStrict(params.origin) },\n ...buildCommonRoutingBody(params),\n ...(params.vehicle && buildCommonVehicleBody(params.vehicle)),\n});\n\nconst buildRequestHeaders = (params: ReachableRangeParams): TomTomAPIHeaders => ({\n ...buildCommonServiceRequestHeaders(params),\n Attributes: ATTRIBUTES,\n});\n\n/**\n * Default function for building a calculate reachable range request from\n * {@link ReachableRangeParams}, targeting the Orbis routing API.\n * @param params The reachable range parameters, with global configuration already merged into them.\n */\nexport const buildReachableRangeRequest = (params: ReachableRangeParams): ReachableRangeRequestAPI => {\n const url = buildUrl(params);\n appendBudget(url.searchParams, params);\n appendOptionalParam(url.searchParams, 'maxFerryLengthInMeters', params.maxFerryLengthMeters);\n appendOptionalParam(url.searchParams, 'smoothing', params.smoothing ?? 'strong');\n if (params.vehicle) appendVehicleQueryParams(url, params.vehicle);\n\n return { method: 'POST', url, data: buildPostData(params), headers: buildRequestHeaders(params) };\n};\n","import type { BBox, PolygonFeature } from '@tomtom-org/maps-sdk/core';\nimport { bboxFromGeoJSON } from '@tomtom-org/maps-sdk/core';\nimport type { Polygon } from 'geojson';\nimport { pick } from 'lodash-es';\nimport type { ReachableRangeResponseAPI } from './types/apiResponseTypes';\nimport type { ReachableRangeParams, ReachableRangeProperties } from './types/reachableRangeParams';\n\n// An allow-list, so credentials and anything else merged in from the global config never reach the result.\n// Written as a record, so a request parameter added to the type without an entry here fails to compile.\nconst propertyKeys = Object.keys({\n origin: true,\n budget: true,\n costModel: true,\n vehicle: true,\n travelMode: true,\n when: true,\n maxFerryLengthMeters: true,\n smoothing: true,\n} satisfies Record<keyof ReachableRangeProperties, true>) as (keyof ReachableRangeProperties)[];\n\n/**\n * @param params The request parameters, with global configuration already merged into them.\n */\nexport const parseReachableRangeResponse = (\n apiResponse: ReachableRangeResponseAPI,\n params: ReachableRangeParams,\n): PolygonFeature<ReachableRangeProperties> => {\n const geometry: Polygon = {\n type: 'Polygon',\n coordinates: [apiResponse.reachableRange.boundary.map((point) => [point.longitude, point.latitude])],\n };\n const bbox = bboxFromGeoJSON(geometry) as BBox; // the geometry should always have a bbox\n return { type: 'Feature', geometry, bbox, properties: pick(params, propertyKeys) };\n};\n","import type { PolygonFeature } from '@tomtom-org/maps-sdk/core';\nimport { parseRoutingResponseError } from '../routing/routingResponseErrorParser';\nimport type { ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { reachableRangeRequestValidationConfig } from './reachableRangeRequestSchema';\nimport { buildReachableRangeRequest } from './requestBuilder';\nimport { parseReachableRangeResponse } from './responseParser';\nimport type { ReachableRangeRequestAPI } from './types/apiRequestTypes';\nimport type { ReachableRangeResponseAPI } from './types/apiResponseTypes';\nimport type { ReachableRangeParams, ReachableRangeProperties } from './types/reachableRangeParams';\n\n/**\n * Reachable range service template type.\n * @ignore\n */\nexport type ReachableRangeTemplate = ServiceTemplate<\n ReachableRangeParams,\n ReachableRangeRequestAPI,\n ReachableRangeResponseAPI,\n PolygonFeature<ReachableRangeProperties>\n>;\n\n/**\n * Reachable range service template main implementation.\n * @ignore\n */\nexport const reachableRangeTemplate: ReachableRangeTemplate = {\n requestValidation: reachableRangeRequestValidationConfig,\n buildRequest: buildReachableRangeRequest,\n sendRequest: fetchWith,\n parseResponse: parseReachableRangeResponse,\n parseResponseError: parseRoutingResponseError,\n apiVersion: 3,\n};\n","import { bboxFromGeoJSON, PolygonFeature, PolygonFeatures } from '@tomtom-org/maps-sdk/core';\nimport { SDKServiceError } from '../shared/errors';\nimport { callService } from '../shared/serviceTemplate';\nimport { reachableRangeTemplate } from './reachableRangeTemplate';\nimport type { ReachableRangeParams, ReachableRangeProperties } from './types/reachableRangeParams';\n\n/**\n * Calculate the area reachable from a starting point within given constraints.\n *\n * The Reachable Range service (also known as isochrone) computes a polygon representing\n * all locations that can be reached from an origin within specified limits of time, distance,\n * or energy consumption. This is essential for range analysis, service area visualization,\n * and logistics planning.\n *\n * @remarks\n * **Private Preview**: needs an API key with Calculate Reachable Range access, unavailable on a Freemium or\n * Pay As You Grow (PAYG) basis: [request it from TomTom Sales](https://www.tomtom.com/contact-sales/?source_app=docs-portal&source_product=calculate-reachable-range).\n *\n * Key features:\n * - **Time-based ranges**: Areas reachable within a time budget (e.g., 30 minutes)\n * - **Distance-based ranges**: Areas within a distance limit (e.g., 50km)\n * - **Energy-based ranges**: EV range considering battery consumption\n * - **Traffic awareness**: Accounts for current and historic traffic patterns\n * - **Vehicle-specific**: Considers vehicle characteristics and constraints\n *\n * Common use cases:\n * - **Service area mapping**: \"Show areas we can deliver to within 1 hour\"\n * - **EV range visualization**: Display drivable range on current battery\n * - **Real estate**: \"Find homes within 45 minutes of workplace\"\n * - **Emergency services**: Calculate ambulance/fire truck response areas\n * - **Market analysis**: Identify customer catchment areas\n * - **Location planning**: Optimize facility placement based on coverage\n *\n * @param params Reachable range parameters including origin and budget constraints\n *\n * @returns Promise resolving to a polygon representing the reachable area\n *\n * @example\n * ```typescript\n * // Calculate 30-minute drive time range\n * const range30min = await calculateReachableRange({\n * origin: [4.9041, 52.3676], // Amsterdam\n * budget: { type: 'timeMinutes', value: 30 }\n * });\n *\n * // EV range down to 20% remaining battery\n * const evRange = await calculateReachableRange({\n * origin: [4.9041, 52.3676],\n * budget: { type: 'remainingChargePCT', value: 20 },\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * engine: {\n * consumption: {\n * speedsToConsumptionsKWH: [\n * { speedKMH: 50, consumptionUnitsPer100KM: 8 },\n * { speedKMH: 80, consumptionUnitsPer100KM: 12 }\n * ]\n * },\n * charging: { maxChargeKWH: 60 }\n * }\n * },\n * state: { currentChargePCT: 80 }\n * }\n * });\n *\n * // Traffic-aware range at departure time\n * const morningRange = await calculateReachableRange({\n * origin: [4.9041, 52.3676],\n * budget: { type: 'timeMinutes', value: 45 },\n * when: { option: 'departAt', date: new Date('2025-10-20T08:00:00Z') } // Rush hour\n * });\n * ```\n *\n * @see [Reachable Range API Documentation](https://developer.tomtom.com/routing-api/documentation/tomtom-orbis-maps/v3/calculate-reachable-range) (requires an enterprise login)\n * @see [Reachable Ranges Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/routing/reachable-ranges)\n *\n * @group Routing\n */\nexport const calculateReachableRange = async (\n params: ReachableRangeParams,\n): Promise<PolygonFeature<ReachableRangeProperties>> => callService(params, reachableRangeTemplate, 'Reachable Range');\n\n// Either signal cancels the call. A lone signal passes through unwrapped, so a caller still\n// sees the identical signal it handed in.\nconst combineSignals = (...signals: (AbortSignal | undefined)[]): AbortSignal | undefined => {\n const live = signals.filter((signal): signal is AbortSignal => !!signal);\n return live.length < 2 ? live[0] : AbortSignal.any(live);\n};\n\n/**\n * Calculate multiple reachable range areas from different origins or with different constraints.\n *\n * Computes several isochrone polygons in sequence, useful for comparing ranges from multiple\n * locations, visualizing multiple time/distance rings from the same origin, or analyzing\n * different vehicle scenarios.\n *\n * @remarks\n * **Private Preview**: needs the same API key access as {@link calculateReachableRange}.\n *\n * Use cases:\n * - **Multi-location comparison**: Compare service areas of different store locations\n * - **Concentric ranges**: Create 15/30/45 minute drive time rings from one origin\n * - **Scenario analysis**: Compare ranges for different vehicle types or times of day\n * - **Coverage optimization**: Find optimal facility locations with minimal overlap\n *\n * Note: Ranges are calculated sequentially to avoid rate limiting. For large batches,\n * consider implementing your own parallel processing with appropriate throttling.\n *\n * @param paramsArray Array of reachable range parameters, one for each area to calculate\n * @param options.signal An `AbortSignal` to cancel the in-flight request and skip the remaining ones.\n * Composed with any per-entry `signal`, so either one cancels.\n *\n * @returns Promise resolving to a FeatureCollection of reachable area polygons\n *\n * @example\n * ```typescript\n * // Calculate concentric drive time rings (15, 30, 45 minutes)\n * const timeRings = await calculateReachableRanges([\n * { origin: [4.9, 52.3], budget: { type: 'timeMinutes', value: 15 } },\n * { origin: [4.9, 52.3], budget: { type: 'timeMinutes', value: 30 } },\n * { origin: [4.9, 52.3], budget: { type: 'timeMinutes', value: 45 } }\n * ]);\n *\n * // Compare service areas of multiple stores\n * const storeRanges = await calculateReachableRanges([\n * { origin: [4.9, 52.3], budget: { type: 'timeMinutes', value: 30 } },\n * { origin: [4.5, 51.9], budget: { type: 'timeMinutes', value: 30 } },\n * { origin: [5.1, 52.1], budget: { type: 'timeMinutes', value: 30 } }\n * ]);\n *\n * // Compare the range spent on 10% versus 20% of the battery\n * const electricVehicle = {\n * engineType: 'electric' as const,\n * model: {\n * engine: {\n * consumption: { speedsToConsumptionsKWH: [{ speedKMH: 80, consumptionUnitsPer100KM: 12 }] },\n * charging: { maxChargeKWH: 60 }\n * }\n * }\n * };\n * const batteryRanges = await calculateReachableRanges([\n * { origin: [4.9, 52.3], budget: { type: 'spentChargePCT', value: 10 }, vehicle: electricVehicle },\n * { origin: [4.9, 52.3], budget: { type: 'spentChargePCT', value: 20 }, vehicle: electricVehicle }\n * ]);\n * ```\n *\n * @see [Reachable Range API Documentation](https://developer.tomtom.com/routing-api/documentation/tomtom-orbis-maps/v3/calculate-reachable-range) (requires an enterprise login)\n * @see [Reachable Ranges Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/routing/reachable-ranges)\n *\n * @group Routing\n */\nexport const calculateReachableRanges = async (\n paramsArray: ReachableRangeParams[],\n options?: { signal?: AbortSignal },\n): Promise<PolygonFeatures<ReachableRangeProperties>> => {\n const features: PolygonFeature<ReachableRangeProperties>[] = [];\n for (const params of paramsArray) {\n const signal = combineSignals(params.signal, options?.signal);\n try {\n // we sequentially fetch reachable ranges (less speed but better to prevent QPS limit breaches):\n features.push(await calculateReachableRange({ ...params, ...(signal && { signal }) }));\n } catch (error) {\n // Re-throw non-API errors (e.g. validation/programming errors) and critical HTTP errors\n // (403 Forbidden, 429 Too Many Requests) — only silently skip API errors that indicate\n // no reachable range could be computed at this location (e.g. no road network).\n if (!(error instanceof SDKServiceError) || error.status === 403 || error.status === 429) {\n throw error;\n }\n }\n }\n const bbox = bboxFromGeoJSON(features);\n return { type: 'FeatureCollection', ...(bbox && { bbox }), features };\n};\n","import type { GeographyType } from '@tomtom-org/maps-sdk/core';\nimport { getPositionStrict } from '@tomtom-org/maps-sdk/core';\nimport { isNil } from 'lodash-es';\nimport type { GetObject } from '../shared';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { ReverseGeocodingAreaTypeAPI } from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n// Maps the SDK's public GeographyType values to the API's `areaTypes` values.\nexport const AREA_TYPE_BY_GEOGRAPHY_TYPE: Record<GeographyType, ReverseGeocodingAreaTypeAPI> = {\n Country: 'country',\n CountrySubdivision: 'countrySubdivision',\n CountrySecondarySubdivision: 'countrySecondarySubdivision',\n CountryTertiarySubdivision: 'countryTertiarySubdivision',\n Municipality: 'municipality',\n MunicipalitySubdivision: 'municipalitySubdivision',\n MunicipalitySecondarySubdivision: 'municipalitySecondarySubdivision',\n Neighbourhood: 'neighborhood',\n PostalCodeArea: 'postalCode',\n};\n\n// Requests every result field the SDK's response parser understands, so callers get a\n// complete place without having to specify a projection themselves.\nconst DEFAULT_ATTRIBUTES = 'results(*)';\n\nconst buildUrlBasePath = (params: ReverseGeocodingParams): string => resolvePlacesEndpointUrl(params, 'reverseGeocode');\n\n/**\n * Default function for building a reverse geocoding request from {@link ReverseGeocodingParams}\n * @param params The reverse geocoding parameters, with global configuration already merged into them.\n */\nexport const buildRevGeoRequest = (params: ReverseGeocodingParams): GetObject => {\n const [lng, lat] = getPositionStrict(params.position);\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n urlParams.append('position', `${lng},${lat}`);\n\n !isNil(params.radiusMeters) && urlParams.append('radiusInMeters', String(params.radiusMeters));\n params.geographyTypes &&\n urlParams.append(\n 'areaTypes',\n params.geographyTypes.map((geographyType) => AREA_TYPE_BY_GEOGRAPHY_TYPE[geographyType]).join(','),\n );\n !isNil(params.heading) && urlParams.append('vehicleHeadingInDegrees', String(params.heading));\n params.view && urlParams.append('geopoliticalView', params.view);\n\n return {\n url,\n headers: {\n ...buildCommonServiceRequestHeaders(params),\n Attributes: DEFAULT_ATTRIBUTES,\n },\n };\n};\n","const ISO2_TO_ISO3: Record<string, string> = {\n AF: 'AFG',\n AX: 'ALA',\n AL: 'ALB',\n DZ: 'DZA',\n AS: 'ASM',\n AD: 'AND',\n AO: 'AGO',\n AI: 'AIA',\n AQ: 'ATA',\n AG: 'ATG',\n AR: 'ARG',\n AM: 'ARM',\n AW: 'ABW',\n AU: 'AUS',\n AT: 'AUT',\n AZ: 'AZE',\n BS: 'BHS',\n BH: 'BHR',\n BD: 'BGD',\n BB: 'BRB',\n BY: 'BLR',\n BE: 'BEL',\n BZ: 'BLZ',\n BJ: 'BEN',\n BM: 'BMU',\n BT: 'BTN',\n BO: 'BOL',\n BQ: 'BES',\n BA: 'BIH',\n BW: 'BWA',\n BV: 'BVT',\n BR: 'BRA',\n IO: 'IOT',\n BN: 'BRN',\n BG: 'BGR',\n BF: 'BFA',\n BI: 'BDI',\n CV: 'CPV',\n KH: 'KHM',\n CM: 'CMR',\n CA: 'CAN',\n KY: 'CYM',\n CF: 'CAF',\n TD: 'TCD',\n CL: 'CHL',\n CN: 'CHN',\n CX: 'CXR',\n CC: 'CCK',\n CO: 'COL',\n KM: 'COM',\n CG: 'COG',\n CD: 'COD',\n CK: 'COK',\n CR: 'CRI',\n CI: 'CIV',\n HR: 'HRV',\n CU: 'CUB',\n CW: 'CUW',\n CY: 'CYP',\n CZ: 'CZE',\n DK: 'DNK',\n DJ: 'DJI',\n DM: 'DMA',\n DO: 'DOM',\n EC: 'ECU',\n EG: 'EGY',\n SV: 'SLV',\n GQ: 'GNQ',\n ER: 'ERI',\n EE: 'EST',\n SZ: 'SWZ',\n ET: 'ETH',\n FK: 'FLK',\n FO: 'FRO',\n FJ: 'FJI',\n FI: 'FIN',\n FR: 'FRA',\n GF: 'GUF',\n PF: 'PYF',\n TF: 'ATF',\n GA: 'GAB',\n GM: 'GMB',\n GE: 'GEO',\n DE: 'DEU',\n GH: 'GHA',\n GI: 'GIB',\n GR: 'GRC',\n GL: 'GRL',\n GD: 'GRD',\n GP: 'GLP',\n GU: 'GUM',\n GT: 'GTM',\n GG: 'GGY',\n GN: 'GIN',\n GW: 'GNB',\n GY: 'GUY',\n HT: 'HTI',\n HM: 'HMD',\n VA: 'VAT',\n HN: 'HND',\n HK: 'HKG',\n HU: 'HUN',\n IS: 'ISL',\n IN: 'IND',\n ID: 'IDN',\n IR: 'IRN',\n IQ: 'IRQ',\n IE: 'IRL',\n IM: 'IMN',\n IL: 'ISR',\n IT: 'ITA',\n JM: 'JAM',\n JP: 'JPN',\n JE: 'JEY',\n JO: 'JOR',\n KZ: 'KAZ',\n KE: 'KEN',\n KI: 'KIR',\n KP: 'PRK',\n KR: 'KOR',\n KW: 'KWT',\n KG: 'KGZ',\n LA: 'LAO',\n LV: 'LVA',\n LB: 'LBN',\n LS: 'LSO',\n LR: 'LBR',\n LY: 'LBY',\n LI: 'LIE',\n LT: 'LTU',\n LU: 'LUX',\n MO: 'MAC',\n MG: 'MDG',\n MW: 'MWI',\n MY: 'MYS',\n MV: 'MDV',\n ML: 'MLI',\n MT: 'MLT',\n MH: 'MHL',\n MQ: 'MTQ',\n MR: 'MRT',\n MU: 'MUS',\n YT: 'MYT',\n MX: 'MEX',\n FM: 'FSM',\n MD: 'MDA',\n MC: 'MCO',\n MN: 'MNG',\n ME: 'MNE',\n MS: 'MSR',\n MA: 'MAR',\n MZ: 'MOZ',\n MM: 'MMR',\n NA: 'NAM',\n NR: 'NRU',\n NP: 'NPL',\n NL: 'NLD',\n NC: 'NCL',\n NZ: 'NZL',\n NI: 'NIC',\n NE: 'NER',\n NG: 'NGA',\n NU: 'NIU',\n NF: 'NFK',\n MK: 'MKD',\n MP: 'MNP',\n NO: 'NOR',\n OM: 'OMN',\n PK: 'PAK',\n PW: 'PLW',\n PS: 'PSE',\n PA: 'PAN',\n PG: 'PNG',\n PY: 'PRY',\n PE: 'PER',\n PH: 'PHL',\n PN: 'PCN',\n PL: 'POL',\n PT: 'PRT',\n PR: 'PRI',\n QA: 'QAT',\n RE: 'REU',\n RO: 'ROU',\n RU: 'RUS',\n RW: 'RWA',\n BL: 'BLM',\n SH: 'SHN',\n KN: 'KNA',\n LC: 'LCA',\n MF: 'MAF',\n PM: 'SPM',\n VC: 'VCT',\n WS: 'WSM',\n SM: 'SMR',\n ST: 'STP',\n SA: 'SAU',\n SN: 'SEN',\n RS: 'SRB',\n SC: 'SYC',\n SL: 'SLE',\n SG: 'SGP',\n SX: 'SXM',\n SK: 'SVK',\n SI: 'SVN',\n SB: 'SLB',\n SO: 'SOM',\n ZA: 'ZAF',\n GS: 'SGS',\n SS: 'SSD',\n ES: 'ESP',\n LK: 'LKA',\n SD: 'SDN',\n SR: 'SUR',\n SJ: 'SJM',\n SE: 'SWE',\n CH: 'CHE',\n SY: 'SYR',\n TW: 'TWN',\n TJ: 'TJK',\n TZ: 'TZA',\n TH: 'THA',\n TL: 'TLS',\n TG: 'TGO',\n TK: 'TKL',\n TO: 'TON',\n TT: 'TTO',\n TN: 'TUN',\n TR: 'TUR',\n TM: 'TKM',\n TC: 'TCA',\n TV: 'TUV',\n UG: 'UGA',\n UA: 'UKR',\n AE: 'ARE',\n GB: 'GBR',\n US: 'USA',\n UM: 'UMI',\n UY: 'URY',\n UZ: 'UZB',\n VU: 'VUT',\n VE: 'VEN',\n VN: 'VNM',\n VG: 'VGB',\n VI: 'VIR',\n WF: 'WLF',\n EH: 'ESH',\n YE: 'YEM',\n ZM: 'ZMB',\n ZW: 'ZWE',\n};\n\nexport const toIso3 = (iso2: string): string => ISO2_TO_ISO3[iso2] ?? iso2;\n","import type { GeographyType } from '@tomtom-org/maps-sdk/core';\nimport { generateId, getPositionStrict, toPointGeometry } from '@tomtom-org/maps-sdk/core';\nimport { toIso3 } from '../shared/iso2ToIso3';\nimport { AREA_TYPE_BY_GEOGRAPHY_TYPE } from './requestBuilder';\nimport type { ReverseGeocodingResponse } from './reverseGeocoding';\nimport type {\n ReverseGeocodingAddressAPI,\n ReverseGeocodingAreaTypeAPI,\n ReverseGeocodingResponseAPI,\n ReverseGeocodingResultTypeAPI,\n} from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\nconst PLACE_TYPE_BY_RESULT_TYPE: Record<ReverseGeocodingResultTypeAPI, 'Street' | 'Geography' | 'Point Address'> = {\n street: 'Street',\n area: 'Geography',\n address: 'Point Address',\n};\n\nconst GEOGRAPHY_TYPE_BY_AREA_TYPE = Object.fromEntries(\n Object.entries(AREA_TYPE_BY_GEOGRAPHY_TYPE).map(([geographyType, areaType]) => [areaType, geographyType]),\n) as Record<ReverseGeocodingAreaTypeAPI, GeographyType>;\n\n// Renames the handful of v2 address fields whose SDK-facing name differs from the API's, and\n// derives `countryCodeISO3`, which v2 does not return but the v1 search services do. Same\n// derivation the routing response parser applies to its own `countryCodeIso2` fields.\nconst mapAddress = (apiAddress: ReverseGeocodingAddressAPI) => {\n const { street, houseNumber, countryCodeIso2, ...rest } = apiAddress;\n return {\n ...rest,\n ...(street && { streetName: street }),\n ...(houseNumber && { streetNumber: houseNumber }),\n ...(countryCodeIso2 && { countryCode: countryCodeIso2, countryCodeISO3: toIso3(countryCodeIso2) }),\n };\n};\n\n/**\n * Default method for parsing reverse geocoding request from {@link ReverseGeocodingResponse}\n * @param params\n * @param apiResponse\n */\nexport const parseRevGeoResponse = (\n apiResponse: ReverseGeocodingResponseAPI,\n params: ReverseGeocodingParams,\n): ReverseGeocodingResponse => {\n const firstResult = apiResponse.results[0];\n return {\n type: 'Feature',\n // The requested coordinates are the primary ones, and set as the GeoJSON Feature geometry:\n geometry: toPointGeometry(getPositionStrict(params.position)),\n id: firstResult?.id ?? generateId(),\n ...(firstResult && {\n properties: {\n type: PLACE_TYPE_BY_RESULT_TYPE[firstResult.type],\n address: { freeformAddress: firstResult.title, ...mapAddress(firstResult.address) },\n // v2 has no `dataSources`, and an area result's `id` is not a Geometry Data ID: the\n // service answers it with \"Requested geometry not found\".\n ...(firstResult.areaType && { geographyType: [GEOGRAPHY_TYPE_BY_AREA_TYPE[firstResult.areaType]] }),\n // v2 calls them access points and places them on the road network; they are the\n // same entrances the search services return, minus the main/minor classification.\n ...(firstResult.accessPoints?.length && {\n entryPoints: firstResult.accessPoints.map((accessPoint) => ({\n position: accessPoint.position.coordinates,\n })),\n }),\n // The reverse geocoded coordinates are secondary and set in the GeoJSON properties:\n originalPosition: firstResult.position.coordinates,\n },\n }),\n };\n};\n","import { geographyTypes, views } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { hasLngLatSchema } from '../shared/schema/geometriesSchema';\n\nconst revGeocodeRequestMandatory = z.object({\n position: hasLngLatSchema,\n});\n\nconst revGeocodeRequestOptional = z.object({\n geographyTypes: z.array(z.enum(geographyTypes)).optional(),\n heading: z.number().min(-360).max(360).optional(),\n radiusMeters: z.number().positive().max(5_000_000).optional(),\n view: z.enum(views).optional(),\n});\n\n/**\n * @ignore\n */\nexport const revGeocodeRequestSchema = revGeocodeRequestMandatory.extend(revGeocodeRequestOptional.shape);\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { buildRevGeoRequest } from './requestBuilder';\nimport { parseRevGeoResponse } from './responseParser';\nimport type { ReverseGeocodingResponse } from './reverseGeocoding';\nimport { revGeocodeRequestSchema } from './revGeocodeRequestSchema';\nimport type { ReverseGeocodingResponseAPI } from './types/apiTypes';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n/**\n * Reverse Geocoding service template type.\n * @ignore\n */\nexport type ReverseGeocodingTemplate = ServiceTemplate<\n ReverseGeocodingParams,\n GetObject,\n ReverseGeocodingResponseAPI,\n ReverseGeocodingResponse\n>;\n\n/**\n * Reverse Geocoding service template main implementation.\n * @ignore\n */\nexport const reverseGeocodingTemplate: ReverseGeocodingTemplate = {\n requestValidation: { schema: revGeocodeRequestSchema },\n buildRequest: buildRevGeoRequest,\n apiVersion: 2,\n sendRequest: get,\n parseResponse: parseRevGeoResponse,\n};\n","import type { Place, RevGeoAddressProps } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { reverseGeocodingTemplate } from './reverseGeocodingTemplate';\nimport type { ReverseGeocodingParams } from './types/reverseGeocodingParams';\n\n/**\n * Response from the reverse geocoding service.\n *\n * Contains a place with address information for the given coordinates.\n *\n * @group Reverse Geocoding\n */\nexport type ReverseGeocodingResponse = Place<RevGeoAddressProps>;\n\n/**\n * Convert geographic coordinates into human-readable addresses (reverse geocoding).\n *\n * Reverse geocoding translates latitude/longitude coordinates into street addresses,\n * which is essential for location-based applications that need to display addresses\n * from GPS coordinates or map clicks.\n *\n * @remarks\n * Common use cases:\n * - **Tracking applications**: Convert GPS coordinates from devices into addresses\n * - **Map interactions**: Display address when user clicks on map\n * - **Location sharing**: Show readable location instead of coordinates\n * - **Delivery apps**: Confirm pickup/dropoff addresses from driver location\n * - **Asset tracking**: Display current location of vehicles or equipment\n *\n * Features:\n * - Returns complete address hierarchy (street, city, state, country)\n * - Returns the result type (address, street, or geography)\n * - Narrows results to administrative areas with `geographyTypes`\n * - Direction-aware street matching with `heading`\n *\n * @param params - Reverse geocoding parameters including coordinates\n *\n * @returns Promise resolving to the address for the given coordinates\n *\n * @example\n * ```typescript\n * // Get address for coordinates\n * const address = await reverseGeocode({\n * key: 'your-api-key',\n * position: [4.9041, 52.3676] // Amsterdam coordinates\n * });\n * // Returns: Dam, 1012 Amsterdam, Netherlands\n *\n * // Get the address matched along a given travel direction\n * const specificAddress = await reverseGeocode({\n * key: 'your-api-key',\n * position: [-77.0369, 38.8977], // Washington DC\n * heading: 90 // Traveling east\n * });\n *\n * // Get the containing municipality instead of the street\n * const municipality = await reverseGeocode({\n * key: 'your-api-key',\n * position: [-74.0060, 40.7128], // New York\n * geographyTypes: ['Municipality']\n * });\n * ```\n *\n * @see [Reverse Geocode API Documentation](https://docs.tomtom.com/search-api/documentation/reverse-geocoding-service/reverse-geocode)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Reverse Geocoding Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/reverse-geocoding)\n *\n * @group Reverse Geocoding\n */\nexport const reverseGeocode = async (params: ReverseGeocodingParams): Promise<ReverseGeocodingResponse> =>\n callService(params, reverseGeocodingTemplate, 'ReverseGeocode');\n","import {\n bboxFromGeoJSON,\n getPositionStrict,\n getRoutePlanningLocationType,\n type HasBBox,\n type HasLngLat,\n type LegSectionProps,\n type PathLike,\n type RoutePlanningLocation,\n type TomTomAPIHeaders,\n type WaypointLike,\n type WaypointProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { Feature, LineString, MultiPoint, Point } from 'geojson';\nimport { isNil } from 'lodash-es';\nimport type { FetchInput, VehicleParameters } from '../shared';\nimport { buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport {\n appendVehicleEnergyParams,\n appendVehicleQueryParams,\n buildCommonRoutingBody,\n buildCommonVehicleBody,\n predefinedModelIdOf,\n} from '../shared/request/routingBodyBuilder';\nimport {\n chargingPreferenceParams,\n chargingPreferencesOf,\n electricEngineOf,\n setEnergyParams,\n} from '../shared/request/vehicleEnergyParams';\nimport type {\n AvoidAreasAPI,\n CalculateRoutePOSTDataAPI,\n LegRequestAPI,\n RouteStopRequestAPI,\n} from './types/apiRequestTypes';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\nimport type { RouteStopOptions } from './types/routeStopOptions';\n\nconst buildAvoidAreas = (avoidAreas: HasBBox[]): AvoidAreasAPI => ({\n rectangles: avoidAreas.map((rectangle) => {\n const bbox = bboxFromGeoJSON(rectangle);\n if (!bbox) throw new Error('Could not derive a bounding box from an avoidAreas rectangle');\n\n const [minLon, minLat, maxLon, maxLat] = bbox;\n return { type: 'Feature' as const, bbox: [minLon, minLat, maxLon, maxLat], geometry: null };\n }),\n});\n\n// Section types that are EXPLICIT — they must be individually listed in the Attributes header\n// to be included in the response, even when the parent `sections` is already requested.\n// `lanes` is also EXPLICIT but is guidance-only and handled separately below.\nconst EXPLICIT_SECTION_TYPES = ['tollVignette', 'tollRoad', 'roadShields', 'importantRoadStretch'] as const;\n\nconst buildAttributesHeader = (params: CalculateRouteParams): string => {\n const parts = ['summary', 'legs(summary,path)'];\n const sectionTypes = params.sectionTypes;\n // All non-EXPLICIT section types come back when sections is requested.\n // Omit sections only when the caller explicitly passes an empty sectionTypes array.\n const requestSections = !sectionTypes || sectionTypes.length > 0;\n if (requestSections) {\n parts.push('sections');\n // EXPLICIT section sub-attributes must be listed individually in the Attributes header.\n // When sectionTypes is undefined the customer wants everything, so request all EXPLICIT types\n // (preserving V2 behavior). When sectionTypes is a specific list, only add the requested ones.\n const wantAll = !sectionTypes;\n for (const type of EXPLICIT_SECTION_TYPES) {\n if (wantAll || sectionTypes.includes(type)) {\n parts.push(`sections.${type}`);\n }\n }\n // sections.lanes is EXPLICIT and only available with guidance.\n if (params.guidance && (wantAll || sectionTypes.includes('lanes'))) {\n parts.push('sections.lanes');\n }\n }\n // There is no per-representation `extendedRouteRepresentation` parameter: `progressPoints`\n // is all-or-nothing, so a non-empty `extendedRouteRepresentations` array coarsens to \"give me\n // progress points\". An explicitly empty array is the caller opting out, and saves the payload.\n if (params.extendedRouteRepresentations?.length !== 0) parts.push('progressPoints');\n // When guidance is requested, include the EXPLICIT instruction sub-attributes that back\n // fields the V2 SDK exposed: road-shield icon references, signpost exit icon\n // (→ signpostRoadShieldReferences), and the traffic-light offset.\n if (params.guidance) {\n parts.push(\n 'instructions(*,nextRoadInformation.roadShields.iconReference,previousRoadInformation.roadShields.iconReference,signpost.exitIconReference,distanceToPreviousTrafficLightInMeters)',\n );\n }\n // `roadShieldAtlasReference` is an EXPLICIT top-level attribute (sibling of `routes`), used to\n // build road-shield images; request it whenever guidance instructions are requested.\n const routesAttr = `routes(${parts.join(',')})`;\n return params.guidance ? `roadShieldAtlasReference,${routesAttr}` : routesAttr;\n};\n\n// ─── Regular route (/routes/calculate) vehicle body helper ───\n\n// Every field but the heading is shared with the other two endpoints. `/calculateReachableRange`\n// answers `400 Unknown JSON field` for `vehicleHeadingInDegrees`, so it is added only here.\nconst buildVehicleBody = (vehicle: VehicleParameters): Partial<CalculateRoutePOSTDataAPI> => {\n // 0 is due north, so the heading is tested for presence rather than truthiness.\n const heading = vehicle.state?.heading;\n\n return {\n ...buildCommonVehicleBody(vehicle),\n ...(!isNil(heading) && { vehicleHeadingInDegrees: heading }),\n };\n};\n\n// ─── LDEVR (/calculateLongDistanceEVRoute) URL param helpers ─────────────────\n\ntype LDEVRParams = CalculateRouteParams & { vehicle: VehicleParameters };\n\nconst appendLDEVRVehicleParams = (url: URL, vehicle: VehicleParameters): void => {\n appendVehicleQueryParams(url, vehicle);\n setEnergyParams(url.searchParams, chargingPreferenceParams(vehicle));\n};\n\n// Charging preferences alone select this endpoint. `chargingStopsStrategy` deliberately does NOT:\n// LDEVR also requires `minChargeAtDestinationInkWh`, which only the preferences supply, so\n// triggering the endpoint on the strategy alone builds a request the API is guaranteed to reject.\n// The pairing is enforced in the request schema instead, which calls this same predicate so that\n// the endpoint the builder picks and the request the schema accepts can never disagree.\nexport const isLDEVRRequest = (params: CalculateRouteParams): params is LDEVRParams =>\n !!params.vehicle && !!chargingPreferencesOf(params.vehicle);\n\nexport const PREDEFINED_MODEL_WITHOUT_LDEVR_MESSAGE =\n 'vehicle.model.variantId is only supported for EV routes with charging stops: set vehicle.preferences.chargingPreferences, or describe the vehicle with vehicle.model.dimensions and vehicle.model.engine instead.';\n\n// ─── Route reconstruction helpers ────────────────────────────────────────────\n\n// The wait lives on core's `WaypointProps`, since the map reads it too; the rest is routing-only.\ntype StopOptions = WaypointProps & RouteStopOptions;\n\n// The builder's own intermediate, not a wire shape — hence no `API` suffix and no place in\n// apiRequestTypes.ts: the coordinate becomes `routePlanningLocations`, the options become part of\n// `legs[]`, and only `legToNext` is already wire-shaped.\ntype Stop = { coordinate: [number, number]; legToNext?: LegRequestAPI; options?: StopOptions };\n\nconst getPathCoordinates = (pathLike: PathLike): [number, number][] => {\n if (Array.isArray(pathLike)) return pathLike as [number, number][];\n\n return pathLike.geometry.coordinates as [number, number][];\n};\n\nconst getPathLegSections = (pathLike: PathLike): LegSectionProps[] | undefined => {\n if (Array.isArray(pathLike)) return undefined;\n\n return pathLike.properties?.sections?.leg;\n};\n\nconst expandPathToStops = (pathLike: PathLike): Stop[] => {\n const coordinates = getPathCoordinates(pathLike);\n const legSections = getPathLegSections(pathLike);\n if (\n legSections &&\n legSections.length > 1 &&\n legSections.every((section) => section.startPointIndex != null && section.endPointIndex != null)\n ) {\n const stops: Stop[] = legSections.map((section) => {\n const start = section.startPointIndex ?? 0;\n const end = section.endPointIndex ?? 0;\n return {\n coordinate: coordinates[start],\n legToNext: {\n path: {\n type: 'LineString',\n coordinates: coordinates.slice(start, end + 1),\n },\n },\n };\n });\n stops.push({\n coordinate: coordinates[legSections.at(-1)?.endPointIndex ?? coordinates.length - 1],\n });\n return stops;\n }\n return [\n {\n coordinate: coordinates[0],\n legToNext: { path: { type: 'LineString', coordinates } as LineString },\n },\n { coordinate: coordinates.at(-1) ?? coordinates[0] },\n ];\n};\n\n// Per-stop options ride on the `properties` of a waypoint Feature — see RouteStopOptions.\nconst getRouteStopOptions = (location: RoutePlanningLocation): StopOptions | undefined => {\n if (Array.isArray(location) || !('properties' in location)) return undefined;\n\n return (location as Feature<Point, StopOptions>).properties ?? undefined;\n};\n\nconst flattenLocations = (\n locations: RoutePlanningLocation[],\n useEntryPoint?: CalculateRouteParams['useEntryPoints'],\n): Stop[] => {\n const result: Stop[] = [];\n for (const location of locations) {\n if (getRoutePlanningLocationType(location) === 'waypoint') {\n const options = getRouteStopOptions(location);\n // Explicit candidate entry points are chosen between by the routing engine, so the\n // stop itself must stay the place center rather than a client-side entry point.\n const stopEntryPoint = options?.candidateEntryPoints?.length ? 'ignore' : useEntryPoint;\n const [longitude, latitude] = getPositionStrict(location as WaypointLike, {\n useEntryPoint: stopEntryPoint,\n });\n result.push({ coordinate: [longitude, latitude], ...(options && { options }) });\n } else {\n result.push(...expandPathToStops(location as PathLike));\n }\n }\n return result;\n};\n\nconst toEntryPointsMultiPoint = (entryPoints: HasLngLat[]): MultiPoint => ({\n type: 'MultiPoint',\n coordinates: entryPoints.map((entryPoint) => getPositionStrict(entryPoint)),\n});\n\n// Builds the `legs[]` entry for the leg *arriving at* `arrivingStop`, from that stop's options.\nconst toLegRequest = (arrivingStop: Stop): LegRequestAPI => {\n const options = arrivingStop.options;\n if (!options) return {};\n\n const routeStop: RouteStopRequestAPI = {\n ...(options.pauseDurationSeconds && { pauseDurationInSeconds: options.pauseDurationSeconds }),\n ...(options.candidateEntryPoints?.length && {\n entryPoints: toEntryPointsMultiPoint(options.candidateEntryPoints),\n ...(!isNil(options.preferredEntryPointIndex) && {\n preferredEntryPointIndex: options.preferredEntryPointIndex,\n }),\n }),\n };\n\n return {\n ...(options.legCostModel?.routeType && { routeType: options.legCostModel.routeType }),\n // The route-level `avoids` is a string array; per-leg it takes objects.\n ...(options.legCostModel?.avoid?.length && {\n avoids: options.legCostModel.avoid.map((name) => ({ name })),\n }),\n ...(Object.keys(routeStop).length > 0 && { routeStop }),\n };\n};\n\n// `legs[]` carries route-reconstruction paths and per-stop options; both triggers share the one\n// array, so it is emitted when either applies. Options belong to the stop the leg arrives at,\n// which is what keeps them on the right stop when another stop is inserted earlier in the list.\nconst buildLegs = (allStops: Stop[], hasPathLocations: boolean): LegRequestAPI[] | undefined => {\n // One leg per stop that has something before it, so the index is the departing stop's.\n const arrivingStops = allStops.slice(1);\n const legs = arrivingStops.map((arrivingStop, index) => ({\n ...allStops[index].legToNext,\n ...toLegRequest(arrivingStop),\n }));\n\n if (!hasPathLocations && legs.every((leg) => Object.keys(leg).length === 0)) return undefined;\n\n return legs;\n};\n\nconst buildRoutePlanningLocations = (allStops: Stop[]) => {\n const origin: Point = { type: 'Point', coordinates: allStops[0].coordinate };\n const destination: Point = {\n type: 'Point',\n coordinates: (allStops.at(-1) ?? allStops[0]).coordinate,\n };\n const middleStops = allStops.slice(1, -1);\n return {\n origin,\n destination,\n ...(middleStops.length && {\n waypoints: {\n type: 'MultiPoint' as const,\n coordinates: middleStops.map((stop) => stop.coordinate),\n },\n }),\n };\n};\n\n// ─── Main exported function helpers ──────────────────────────────────────────\n\n// The first spread is what all three routing endpoints share; everything after it is a field\n// `/calculateReachableRange` rejects, which is why it stays here rather than moving to the shared\n// builder.\nconst buildCommonBodyFields = (params: CalculateRouteParams) => ({\n ...buildCommonRoutingBody(params),\n ...(params.maxAlternatives && { maxPathAlternativeRoutes: params.maxAlternatives }),\n ...(params.when?.option === 'arriveBy' && params.when.date && { arrivalDateTime: params.when.date.toISOString() }),\n // The API rejects guidance without a phonetic alphabet; the parser drops the transcriptions.\n ...(params.guidance && { guidance: 'instructions' as const, instructionPhonetics: 'ipa' as const }),\n ...(params.costModel?.avoidAreas?.length && { avoidAreas: buildAvoidAreas(params.costModel.avoidAreas) }),\n ...(params.arrivalSide && {\n arrivalSidePreference: params.arrivalSide === 'curb' ? ('curbSide' as const) : ('anySide' as const),\n }),\n});\n\nconst appendTravelTimeParam = (url: URL, params: CalculateRouteParams): void => {\n if (params.computeTravelTimeFor) url.searchParams.set('computeTravelTimeFor', params.computeTravelTimeFor);\n};\n\nconst buildRequestHeaders = (params: CalculateRouteParams): TomTomAPIHeaders => ({\n ...buildCommonServiceRequestHeaders(params),\n Attributes: buildAttributesHeader(params),\n});\n\nconst buildLDEVRRequest = (\n params: LDEVRParams,\n sharedBody: CalculateRoutePOSTDataAPI,\n headers: TomTomAPIHeaders,\n): FetchInput<CalculateRoutePOSTDataAPI> => {\n const url = new URL(`${params.commonBaseURL}/maps/orbis/routing/calculateLongDistanceEVRoute`);\n appendLDEVRVehicleParams(url, params.vehicle);\n // A query parameter, not a body field — the body rejects it as an unknown JSON field.\n if (params.chargingStopsStrategy) url.searchParams.set('chargingStopsStrategy', params.chargingStopsStrategy);\n\n const chargingModel = electricEngineOf(params.vehicle)?.charging;\n const hasPredefinedModel = !!predefinedModelIdOf(params.vehicle);\n // `chargingParameters` is mandatory on this endpoint, but the endpoint itself is selected by\n // `vehicle.preferences.chargingPreferences` — a different part of the params. Without either a\n // predefined model (the API supplies the charging model for it) or explicit connectors, the\n // request is guaranteed to fail, so fail here with a message that names the actual cause.\n if (!hasPredefinedModel && !chargingModel?.chargingConnectors?.length)\n throw new Error(\n 'Charging preferences require a charging model: set vehicle.model.engine.charging.chargingConnectors (or a predefined vehicle.model.variantId) to plan charging stops.',\n );\n\n // Only ever *adds* to the shared body: every field both endpoints take is built once, before\n // the branch, so a field added there cannot reach one endpoint and silently miss the other.\n const body: CalculateRoutePOSTDataAPI = {\n ...sharedBody,\n ...(chargingModel && {\n chargingParameters: {\n ...(chargingModel.batteryCurve?.length && { batteryCurve: chargingModel.batteryCurve }),\n ...(chargingModel.chargingConnectors?.length && {\n chargingConnectors: [...chargingModel.chargingConnectors],\n }),\n ...(!isNil(chargingModel.chargingTimeOffsetInSec) && {\n chargingTimeOffsetInSec: chargingModel.chargingTimeOffsetInSec,\n }),\n },\n }),\n // This endpoint is electric by definition, whatever the vehicle body derived.\n vehicleEngineType: 'electric',\n };\n\n return { method: 'POST', url, data: body, headers };\n};\n\n// ─── Main exported function ───────────────────────────────────────────────────\n\n/**\n * Default function for building calculate route request from {@link CalculateRouteParams}\n * targeting the Orbis routing API.\n * @param params The calculate route parameters, with global configuration already merged into them.\n */\nexport const buildCalculateRouteRequest = (params: CalculateRouteParams): FetchInput<CalculateRoutePOSTDataAPI> => {\n const hasPathLocations = params.locations.map(getRoutePlanningLocationType).includes('path');\n const allStops = flattenLocations(params.locations, params.useEntryPoints);\n const routePlanningLocations = buildRoutePlanningLocations(allStops);\n const commonBodyFields = buildCommonBodyFields(params);\n const headers = buildRequestHeaders(params);\n // Everything above, plus `legs` and the travel-time param below, is shared by both endpoints:\n // the same CalculateRouteParams must mean the same thing whether or not charging preferences\n // routed it to LDEVR.\n const legs = buildLegs(allStops, hasPathLocations);\n const sharedBody: CalculateRoutePOSTDataAPI = {\n routePlanningLocations,\n ...commonBodyFields,\n ...(legs && { legs }),\n ...(params.vehicle && buildVehicleBody(params.vehicle)),\n };\n if (isLDEVRRequest(params)) {\n const ldevrRequest = buildLDEVRRequest(params, sharedBody, headers);\n appendTravelTimeParam(ldevrRequest.url, params);\n return ldevrRequest;\n }\n\n // `vehicleModelId` is only supported by the LDEVR endpoint, which is reached by setting\n // `vehicle.preferences.chargingPreferences`. Sending it here returns\n // `400 parameter [vehicleModelId] not supported`, and dropping it silently would plan the\n // route for a default vehicle instead of the requested one.\n if (params.vehicle && predefinedModelIdOf(params.vehicle)) throw new Error(PREDEFINED_MODEL_WITHOUT_LDEVR_MESSAGE);\n\n const url = new URL(`${params.commonBaseURL}/maps/orbis/routing/routes/calculate`);\n if (params.vehicle) appendVehicleEnergyParams(url, params.vehicle);\n\n appendTravelTimeParam(url, params);\n\n return { method: 'POST', url, data: sharedBody, headers };\n};\n","import type { GetPositionEntryPointOption, inputSectionTypes, RoutePlanningLocation } from '@tomtom-org/maps-sdk/core';\nimport type { CommonRoutingParams, CommonServiceParams, ElectricVehicleParamsWithChargingStops } from '../../shared';\nimport type { CalculateRouteRequestAPI } from './apiRequestTypes';\nimport type { CalculateRouteResponseAPI } from './apiResponseTypes';\n\n/**\n * Route section type that can be requested in routing parameters.\n *\n * @remarks\n * Note: Some section types (like \"leg\") are automatically included regardless of this parameter.\n *\n * @group Routing\n */\nexport type InputSectionType = (typeof inputSectionTypes)[number];\n\n/**\n * Array of section types to include in the route response.\n *\n * Sections divide the route into portions with specific characteristics, helping you\n * display additional context like toll roads, ferry crossings, or traffic incidents.\n *\n * @remarks\n * Available section types:\n * - `carTrain`: Sections requiring car train transport\n * - `ferry`: Water crossings requiring ferry\n * - `tunnel`: Underground tunnel sections\n * - `motorway`: Highway/freeway sections\n * - `pedestrian`: Pedestrian-only sections\n * - `toll`: Sections requiring toll payment\n * - `tollVignette`: Sections requiring toll vignette\n * - `country`: Different countries traversed\n * - `traffic`: Sections with traffic incidents\n * - `vehicleRestricted`: Sections with vehicle restrictions\n * - `carpool`: HOV/carpool lane sections\n * - `urban`: Urban area sections\n * - `unpaved`: Unpaved road sections\n * - `lowEmissionZone`: Low emission zones\n * - `speedLimit`: Speed limit changes\n * - `roadShields`: Road shield information\n * - `importantRoadStretch`: Named important road stretches along the route\n *\n * @example\n * ```typescript\n * // Request toll, ferry, and traffic sections\n * const sectionTypes: InputSectionTypes = ['toll', 'ferry', 'traffic'];\n *\n * // Request all available sections\n * const sectionTypes: InputSectionTypes = inputSectionTypes;\n *\n * // Request no optional sections (leg sections still included)\n * const sectionTypes: InputSectionTypes = [];\n * ```\n *\n * @remarks\n * A requested section type is only present in the response if the route actually traverses it\n * (e.g. `ferry` is omitted on a route with no water crossing). The `lanes` section is guidance-only:\n * it is returned solely when {@link GuidanceParams | `guidance`} is also requested, and is therefore\n * not part of this input list.\n *\n * @default When omitted, all section types are requested (equivalent to `inputSectionTypes`).\n *\n * @group Routing\n */\nexport type InputSectionTypes = InputSectionType[];\n\n/**\n * The accepted values of {@link ChargingStopsStrategy}.\n * @group Routing\n */\nexport const chargingStopsStrategies = [\n 'automaticFastest',\n 'manualFastest',\n 'automaticFastestWithFallbackToManual',\n] as const;\n\n/**\n * How the routing service should plan charging stops on an electric route.\n *\n * @remarks\n * - `automaticFastest`: the service picks charging stops for the fastest journey. The default.\n * - `manualFastest`: the service plans no charging stops of its own; only stops you pass as\n * waypoints are charged at.\n * - `automaticFastestWithFallbackToManual`: automatic, falling back to manual when no chargeable\n * route exists.\n *\n * Requires `vehicle.preferences.chargingPreferences` to be set as well: the endpoint also needs a\n * minimum charge at the destination, which only the preferences supply.\n * @group Routing\n */\nexport type ChargingStopsStrategy = (typeof chargingStopsStrategies)[number];\n\n/**\n * Configuration for turn-by-turn guidance instructions.\n *\n * @group Routing\n */\nexport type GuidanceParams = {\n /**\n * Guidance instruction format type.\n *\n * Currently only 'coded' format is supported.\n */\n type: 'coded';\n};\n\n/**\n * Extended route representation options.\n *\n * Controls which additional progress information is included at route polyline points.\n *\n * @remarks\n * - `distance`: Cumulative distance from start to each point\n * - `travelTime`: Cumulative travel time from start to each point\n *\n * @example\n * ```typescript\n * // Include both distance and time progress\n * const extended: ExtendedRouteRepresentation[] = ['distance', 'travelTime'];\n * ```\n *\n * @default ['distance', 'travelTime']\n *\n * @group Routing\n */\nexport type ExtendedRouteRepresentation = 'distance' | 'travelTime';\n\n/**\n * Traffic computation mode for alternative travel time estimates.\n *\n * Controls whether to calculate additional travel times using different traffic scenarios.\n *\n * @remarks\n * - `none`: Only return default best-estimate travel time\n * - `all`: Return travel times for multiple traffic scenarios:\n * - No traffic (free flow)\n * - Historic traffic patterns\n * - Live traffic incidents\n *\n * @example\n * ```typescript\n * // Get all traffic-based time estimates\n * const computeTravelTimeFor: ComputeTravelTimeFor = 'all';\n * ```\n *\n * @default 'none'\n *\n * @group Routing\n */\nexport type ComputeTravelTimeFor = 'none' | 'all';\n\n/**\n * Side of the road to arrive on.\n *\n * @group Routing\n */\nexport const arrivalSides = ['any', 'curb'] as const;\n\n/**\n * Which side of the road the route should arrive on at each stop.\n *\n * @remarks\n * - `any`: arrive from either side, whichever is faster\n * - `curb`: arrive on the curb side for the country's driving direction\n *\n * @example\n * ```typescript\n * const arrivalSide: ArrivalSide = 'curb';\n * ```\n *\n * @default 'any'\n *\n * @group Routing\n */\nexport type ArrivalSide = (typeof arrivalSides)[number];\n\n/**\n * Maximum number of alternative routes to calculate.\n *\n * Alternative routes provide different options for traveling between the same origin and destination.\n *\n * @remarks\n * - `0`: Only calculate the best route (default)\n * - `1-5`: Calculate best route plus up to N alternatives\n *\n * More alternatives increase computation time but provide more route options.\n *\n * @example\n * ```typescript\n * // Request up to 2 alternative routes\n * const maxAlternatives: MaxNumberOfAlternatives = 2;\n * ```\n *\n * @default 0\n *\n * @group Routing\n */\nexport type MaxNumberOfAlternatives = 0 | 1 | 2 | 3 | 4 | 5;\n\n/**\n * Parameters for calculating a route between waypoints.\n *\n * Includes origin, destination, optional intermediate waypoints, and various routing options\n * to customize the calculated route based on vehicle type, traffic, and preferences.\n *\n * @example\n * ```typescript\n * // Basic route from A to B\n * const params: CalculateRouteParams = {\n * apiKey: 'your-api-key',\n * locations: [\n * [4.9041, 52.3676], // Amsterdam\n * [4.4777, 51.9244] // Rotterdam\n * ]\n * };\n *\n * // Route with guidance and alternatives\n * const advancedParams: CalculateRouteParams = {\n * apiKey: 'your-api-key',\n * locations: [[4.9041, 52.3676], [4.4777, 51.9244]],\n * guidance: { type: 'coded' },\n * maxAlternatives: 2,\n * costModel: {\n * routeType: 'fast',\n * avoid: ['tollRoads', 'motorways']\n * },\n * when: {\n * option: 'departAt',\n * date: new Date('2025-10-20T08:00:00Z')\n * }\n * };\n *\n * // Electric vehicle route with charging\n * const evParams: CalculateRouteParams = {\n * apiKey: 'your-api-key',\n * locations: [[4.9, 52.3], [8.5, 50.1]],\n * vehicle: {\n * engineType: 'electric',\n * model: {\n * engine: {\n * charging: { maxChargeKWH: 85 },\n * consumption: {\n * speedsToConsumptionsKWH: [\n * { speedKMH: 50, consumptionUnitsPer100KM: 8 },\n * { speedKMH: 80, consumptionUnitsPer100KM: 12 },\n * { speedKMH: 120, consumptionUnitsPer100KM: 18 }\n * ]\n * }\n * }\n * },\n * state: { currentChargeInkWh: 50 }\n * }\n * };\n * ```\n *\n * @group Routing\n */\nexport type CalculateRouteParams = (\n | { chargingStopsStrategy?: never }\n | {\n /**\n * How the service should plan charging stops on an electric route.\n *\n * Only the charging-stops endpoint understands it, and\n * `vehicle.preferences.chargingPreferences` is what selects that endpoint — hence the\n * vehicle this variant requires.\n *\n * @default 'automaticFastest' (the service's own default when unset)\n *\n * @example\n * ```typescript\n * // Plan the fastest journey, choosing charging stops along the way\n * chargingStopsStrategy: 'automaticFastest'\n * ```\n */\n chargingStopsStrategy: ChargingStopsStrategy;\n\n /** An electric vehicle whose charging preferences ask the service to plan stops. */\n vehicle: ElectricVehicleParamsWithChargingStops;\n }\n) &\n CommonServiceParams<CalculateRouteRequestAPI, CalculateRouteResponseAPI> &\n CommonRoutingParams & {\n /**\n * Ordered list of locations (waypoints) and/or path points for route calculation.\n *\n * The route will pass through these locations in the specified order.\n *\n * @remarks\n * Requirements:\n * - Minimum 2 waypoints (origin and destination) OR 1 path with 2+ points\n *\n * Supported formats:\n * - Coordinate arrays: `[longitude, latitude]`\n * - Path arrays for route reconstruction\n * - Waypoint Features, whose properties carry the per-stop options\n *\n * The route ends a leg at every location in this list. There is no circle (soft) waypoint\n * that the route merely passes: the routing API takes a plain point for each one. A path\n * pins the geometry the route must follow, but its endpoints go out as waypoints as well,\n * so it shapes the route without shortening the list of legs.\n *\n * @see [POST data parameters](https://docs.tomtom.com/routing-api/documentation/tomtom-maps/calculate-route#post-data-parameters)\n * @see [Response structure](https://docs.tomtom.com/routing-api/documentation/tomtom-maps/calculate-route#structure-of-a-successful-response)\n *\n * @example\n * ```typescript\n * // Coordinate arrays\n * locations: [[4.9, 52.3], [4.5, 51.9]]\n *\n * // With intermediate stop\n * locations: [[4.9, 52.3], [4.7, 52.1], [4.5, 51.9]]\n *\n * // Path array for route reconstruction\n * locations: [\n * [4.9, 52.3], // Origin waypoint\n * [\n * [4.85, 52.25], [4.80, 52.20], [4.75, 52.15] // Path points between waypoints\n * ],\n * [4.5, 51.9] // Destination waypoint\n * ]\n * ```\n */\n locations: RoutePlanningLocation[];\n\n /**\n * Controls how entry points are used for routing.\n *\n * Entry points represent specific building entrances or facility access points.\n * Using them improves routing accuracy by directing to the correct entrance.\n *\n * @remarks\n * - `main-when-available`: Use main entry point if available, otherwise use place center (recommended for routing)\n * - `ignore`: Always use place center position\n *\n * @default 'main-when-available'\n *\n * @example\n * ```typescript\n * // Route to main entrance when available\n * useEntryPoints: 'main-when-available'\n * ```\n */\n useEntryPoints?: GetPositionEntryPointOption;\n\n /**\n * Which side of the road the route should arrive on.\n *\n * @remarks\n * - `any`: arrive from either side, whichever is faster\n * - `curb`: arrive on the curb side for the country's driving direction, so passengers or\n * deliveries can leave the vehicle without crossing the road\n *\n * Applies to the destination and to every intermediate stop.\n *\n * @default 'any'\n *\n * @example\n * ```typescript\n * // Pull up on the passenger side at each stop\n * arrivalSide: 'curb'\n * ```\n */\n arrivalSide?: ArrivalSide;\n\n /**\n * Request travel time calculations for different traffic scenarios.\n *\n * When set to 'all', the returned route summary will contain extra fields:\n * - `noTrafficTravelTimeInSeconds` – Free-flow (no traffic)\n * - `historicTrafficTravelTimeInSeconds` – Historic traffic patterns\n * - `liveTrafficIncidentsTravelTimeInSeconds` – Current live traffic\n *\n * Useful for comparing traffic impact and displaying \"X minutes saved by leaving now\".\n *\n * @default 'none'\n *\n * @example\n * ```typescript\n * computeTravelTimeFor: 'all'\n * ```\n */\n computeTravelTimeFor?: ComputeTravelTimeFor;\n\n /**\n * Request extended progress information at route polyline points.\n *\n * Includes cumulative distance and/or time from start to each coordinate.\n * When non-empty, the route features will contain additional property \"progress\" with an array of objects containing:\n * - `pointIndex` – Index of the coordinate in the route geometry\n * - `travelTimeInSeconds` - Cumulative travel time from start to this point (if \"travelTime\" is requested)\n * - `distanceInMeters` - Cumulative distance from start to this point (if \"distance\" is requested)\n *\n * Useful for displaying progress during navigation or animating route visualization.\n *\n * @remarks\n * Orbis v3 has no per-representation selector: progress points are all-or-nothing. Any\n * non-empty array therefore requests both `distance` and `travelTime`. Pass an empty array\n * to opt out entirely and keep the progress data out of the response payload.\n *\n * @default ['distance', 'travelTime']\n *\n * @example\n * ```typescript\n * extendedRouteRepresentations: ['distance', 'travelTime']\n *\n * // Opt out of progress points altogether\n * extendedRouteRepresentations: []\n * ```\n */\n extendedRouteRepresentations?: ExtendedRouteRepresentation[];\n\n /**\n * Request turn-by-turn guidance instructions.\n *\n * When specified, the response includes detailed instructions with maneuvers and road\n * names.\n *\n * @example\n * ```typescript\n * guidance: { type: 'coded' }\n * ```\n */\n guidance?: GuidanceParams;\n\n /**\n * Route section types to include in the response.\n *\n * Sections help you display route characteristics like tolls, ferries, or traffic.\n * Leg sections are always included regardless of this parameter. A section type is only\n * present in the response if the route actually traverses it. The `lanes` section is\n * guidance-only and is returned solely when {@link GuidanceParams | `guidance`} is also\n * requested.\n *\n * @default When omitted, all section types are requested.\n *\n * @example\n * ```typescript\n * // Request specific sections (any combination of available section types)\n * sectionTypes: ['toll', 'ferry', 'traffic', 'country', 'roadShields']\n *\n * // Request no optional sections\n * sectionTypes: []\n * ```\n */\n sectionTypes?: InputSectionTypes;\n\n /**\n * Maximum number of alternative routes to calculate.\n *\n * Alternative routes provide different travel options between the same origin and destination.\n * Each alternative is optimized differently (e.g., avoiding highways, minimizing tolls).\n *\n * @remarks\n * - More alternatives increase response time\n * - Alternatives are returned in order of quality (best first)\n * - Not all requests will return the maximum number of alternatives\n *\n * @default 0\n *\n * @example\n * ```typescript\n * // Request up to 2 alternatives\n * maxAlternatives: 2\n * ```\n */\n maxAlternatives?: MaxNumberOfAlternatives;\n };\n","import type { SectionType, Waypoint } from '@tomtom-org/maps-sdk/core';\nimport { getRoutePlanningLocationType, inputSectionTypesWithGuidance } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { predefinedModelIdOf } from '../shared/request/routingBodyBuilder';\nimport { commonRoutingRequestSchema } from '../shared/schema/commonRoutingRequestSchema';\nimport {\n featureSchema,\n geometrySchema,\n hasLngLatSchema,\n lineStringCoordsSchema,\n} from '../shared/schema/geometriesSchema';\nimport type { SchemaRefinement } from '../shared/types/validation';\nimport { isLDEVRRequest, PREDEFINED_MODEL_WITHOUT_LDEVR_MESSAGE } from './requestBuilder';\nimport { arrivalSides, type CalculateRouteParams, chargingStopsStrategies } from './types/calculateRouteParams';\n\nconst waypointLikeSchema = z.union([hasLngLatSchema, geometrySchema]);\nconst pathLikeSchema = z.union([lineStringCoordsSchema, featureSchema]);\n\nconst mandatorySchema = z.object({\n locations: z.array(z.union([waypointLikeSchema, pathLikeSchema])).min(1), // see calculateRouteLocationsRefinement\n});\n\nconst optionalSchema = z.object({\n arrivalSide: z.enum(arrivalSides).optional(),\n chargingStopsStrategy: z.enum(chargingStopsStrategies).optional(),\n computeTravelTimeFor: z.enum(['none', 'all']).optional(),\n maxAlternatives: z.number().min(0).max(5).optional(),\n sectionTypes: z.array(z.enum(inputSectionTypesWithGuidance as [SectionType, ...SectionType[]])).optional(),\n});\n\nconst schema = commonRoutingRequestSchema.extend(mandatorySchema.extend(optionalSchema.shape).shape);\n\nconst locationsRefinement: SchemaRefinement<CalculateRouteParams> = {\n check: (data: CalculateRouteParams): boolean => {\n const types = data.locations.map(getRoutePlanningLocationType);\n const waypointCount = types.filter((type) => type === 'waypoint').length;\n const pathCount = types.filter((type) => type === 'path').length;\n // At least 2 waypoints, OR at least 1 path (reconstruction)\n return waypointCount >= 2 || pathCount >= 1;\n },\n message: 'At least 2 waypoints or 1 path (route reconstruction) is required.',\n};\n\n// The strategy only reaches the wire on the LDEVR endpoint, and only charging preferences select\n// that endpoint. A strategy on its own would be dropped by the request builder, so ask the builder's\n// own predicate rather than re-deciding here: a `chargingPreferences: undefined` that satisfied a\n// key check would pass validation and then silently lose the strategy.\nconst chargingStopsStrategyRefinement: SchemaRefinement<CalculateRouteParams> = {\n check: (data: CalculateRouteParams): boolean => !data.chargingStopsStrategy || isLDEVRRequest(data),\n message:\n 'chargingStopsStrategy: set vehicle.preferences.chargingPreferences as well — an EV route needs a minimum charge at the destination, which only the charging preferences provide.',\n};\n\n// A variant identifies a vehicle in TomTom's database, which only the LDEVR endpoint looks up —\n// `/routes/calculate` answers `400` for it. The rule is the endpoint's, not the vehicle's:\n// `calculateReachableRange` accepts a variant with no charging preferences at all, so it cannot live\n// on the shared vehicle type.\nconst predefinedModelRefinement: SchemaRefinement<CalculateRouteParams> = {\n check: (data: CalculateRouteParams): boolean =>\n !(data.vehicle && predefinedModelIdOf(data.vehicle)) || isLDEVRRequest(data),\n message: PREDEFINED_MODEL_WITHOUT_LDEVR_MESSAGE,\n};\n\n// The routing API requires the last leg's wait to be 0, so a pause on the destination is rejected.\n// Which location is the destination is positional, and `locations` is an array rather than a tuple —\n// callers build it dynamically — so the type cannot carry this and validation does.\nconst destinationPauseRefinement: SchemaRefinement<CalculateRouteParams> = {\n check: (data: CalculateRouteParams): boolean => {\n const destination = data.locations.at(-1);\n if (!destination || getRoutePlanningLocationType(destination) !== 'waypoint') return true;\n\n // A waypoint and a route are both Point/LineString Features, so which one this is cannot be\n // narrowed structurally — the check above is what decides it.\n return !(destination as Waypoint).properties?.pauseDurationSeconds;\n },\n message:\n 'pauseDurationSeconds is not supported on the destination: the routing API requires the wait on the last leg to be 0.',\n};\n\n/**\n * @ignore\n */\nexport const routeRequestValidationConfig = {\n schema,\n refinements: [\n locationsRefinement,\n chargingStopsStrategyRefinement,\n predefinedModelRefinement,\n destinationPauseRefinement,\n ],\n};\n","import {\n type DelayMagnitude,\n type TrafficIncidentCategory,\n type TrafficIncidentRequestCategory,\n trafficIncidentRequestCategories,\n} from '@tomtom-org/maps-sdk/core';\nimport type { DelayMagnitudeAPI, IconCategoryAPI } from './types/apiTrafficResponseTypes';\n\n// Keyed on the request categories, so a new one fails to compile until it has a v2 value.\nconst iconCategoryByCategory: Record<TrafficIncidentRequestCategory, IconCategoryAPI> = {\n other: 'unknown',\n accident: 'accident',\n fog: 'fog',\n danger: 'dangerousConditions',\n rain: 'rain',\n frost: 'ice',\n jam: 'jam',\n 'lane-closed': 'laneClosed',\n 'road-closed': 'roadClosed',\n roadworks: 'roadWorks',\n wind: 'wind',\n flooding: 'flooding',\n 'broken-down-vehicle': 'brokenDownVehicle',\n};\n\n// `Map` lookups throughout, so an inherited key such as `constructor` misses instead of resolving to a function.\nconst iconCategoryLookup = new Map<string, IconCategoryAPI>(Object.entries(iconCategoryByCategory));\n\nconst categoryByIconCategory = new Map<string | undefined, TrafficIncidentCategory>(\n trafficIncidentRequestCategories.map((category) => [iconCategoryByCategory[category], category]),\n);\n\nconst delayMagnitudeByAPIMagnitude: Record<DelayMagnitudeAPI, DelayMagnitude> = {\n unknown: 'unknown',\n minor: 'minor',\n moderate: 'moderate',\n major: 'major',\n undefined: 'indefinite',\n};\n\nconst delayMagnitudeLookup = new Map<string | undefined, DelayMagnitude>(Object.entries(delayMagnitudeByAPIMagnitude));\n\n/**\n * Maps a request category to its Orbis Traffic v2 `iconCategories` value. A value that skipped schema\n * validation and has none is sent unchanged, for the API to reject.\n */\nexport const toIconCategory = (category: TrafficIncidentRequestCategory): string =>\n iconCategoryLookup.get(category) ?? category;\n\n/**\n * Maps an Orbis Traffic v2 `iconCategory` to the SDK category; a missing or undocumented value is `'other'`.\n */\nexport const toTrafficIncidentCategory = (iconCategory: string | undefined): TrafficIncidentCategory =>\n categoryByIconCategory.get(iconCategory) ?? 'other';\n\n/**\n * Maps an Orbis Traffic v2 delay magnitude to the SDK one; a missing or undocumented value is `'unknown'`.\n */\nexport const toDelayMagnitude = (apiMagnitude: string | undefined): DelayMagnitude =>\n delayMagnitudeLookup.get(apiMagnitude) ?? 'unknown';\n","import {\n BBox,\n bboxFromGeoJSON,\n type ChargingStop,\n type CountrySectionProps,\n type CurrentType,\n type DrivingSide,\n type Guidance,\n generateId,\n type ImportantRoadStretchProps,\n type Instruction,\n type InstructionText,\n type Landmark,\n type LaneDirection,\n type LaneSectionProps,\n type LegSectionProps,\n type LegSummary,\n type Maneuver,\n type ManeuverAngle,\n type ManeuverView,\n type PossibleLaneDirection,\n type PossibleLaneSeparator,\n type RoadInformation,\n type RoadInformationProperty,\n type RoadShield,\n type RoadShieldReference,\n type RoadShieldSectionProps,\n type RoundaboutType,\n type Route,\n type RouteSummary,\n type Routes,\n type SectionProps,\n type SectionsProps,\n type SideRoad,\n type SideRoadSide,\n type Signpost,\n type SpeedLimitSectionProps,\n type TollPaymentType,\n type TrafficIncidentCategory,\n type TrafficIncidentTEC,\n type TrafficSectionProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { LineString, Position } from 'geojson';\nimport { isNil, omit } from 'lodash-es';\nimport { toChargingSpeed } from '../shared/ev';\nimport { toIso3 } from '../shared/iso2ToIso3';\nimport { maxChargeKWHOf } from '../shared/request/vehicleEnergyParams';\nimport { toDelayMagnitude, toTrafficIncidentCategory } from '../shared/trafficIncidents';\nimport type {\n BasicSectionAPI,\n CalculateRouteResponseAPI,\n ChargingStopAPI,\n CountrySectionAPI,\n CurrentTypeAPI,\n ImportantRoadStretchSectionAPI,\n InstructionAPI,\n InstructionRoadInformationAPI,\n InstructionRoadShieldAPI,\n InstructionTextAPI,\n LanesSectionAPI,\n LegAPI,\n ManeuverViewAPI,\n ProgressPointAPI,\n RouteAPI,\n SideRoadAPI,\n SpeedLimitSectionAPI,\n SummaryAPI,\n TrafficSectionAPI,\n TravelModeSectionAPI,\n} from './types/apiResponseTypes';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\nconst toCurrentType = (apiCurrentType: CurrentTypeAPI): CurrentType | undefined => {\n switch (apiCurrentType) {\n case 'Direct_Current':\n return 'DC';\n case 'Alternating_Current_1_Phase':\n return 'AC1';\n case 'Alternating_Current_3_Phase':\n return 'AC3';\n default:\n return undefined;\n }\n};\n\nconst toChargingStop = (\n chargingInformationAtEndOfLeg: ChargingStopAPI,\n maxChargeKWH: number | undefined,\n): ChargingStop => {\n const chargingConnectionInfo = chargingInformationAtEndOfLeg.chargingConnectionInfo;\n const chargingParkLocation = chargingInformationAtEndOfLeg.chargingParkLocation;\n const coordinates = [chargingParkLocation.coordinate.longitude, chargingParkLocation.coordinate.latitude];\n\n const addressParts = [chargingParkLocation.street, chargingParkLocation.houseNumber].filter(Boolean);\n const freeformAddress = addressParts.length > 0 ? addressParts.join(', ') : '';\n\n return {\n type: 'Feature',\n id: chargingInformationAtEndOfLeg.chargingParkId,\n geometry: { type: 'Point', coordinates },\n properties: {\n ...omit(chargingInformationAtEndOfLeg, ['chargingConnectionInfo', 'chargingParkLocation']),\n type: 'POI',\n dataSources: { chargingAvailability: { id: chargingInformationAtEndOfLeg.chargingParkUuid } },\n address: {\n freeformAddress,\n ...(chargingParkLocation.street && { streetName: chargingParkLocation.street }),\n ...(chargingParkLocation.houseNumber && { streetNumber: chargingParkLocation.houseNumber }),\n ...(chargingParkLocation.city && { municipality: chargingParkLocation.city }),\n ...(chargingParkLocation.region && { countrySubdivision: chargingParkLocation.region }),\n ...(chargingParkLocation.postalCode && { postalCode: chargingParkLocation.postalCode }),\n ...(chargingParkLocation.country && { country: chargingParkLocation.country }),\n },\n ...(chargingConnectionInfo && {\n chargingConnectionInfo: {\n plugType: chargingConnectionInfo.chargingPlugType,\n currentInA: chargingConnectionInfo.chargingCurrentInA,\n voltageInV: chargingConnectionInfo.chargingVoltageInV,\n chargingPowerInkW: chargingConnectionInfo.chargingPowerInkW,\n currentType: toCurrentType(chargingConnectionInfo.chargingCurrentType),\n chargingSpeed: toChargingSpeed(chargingConnectionInfo.chargingPowerInkW),\n },\n }),\n ...(maxChargeKWH && {\n targetChargeInPCT: (100 * chargingInformationAtEndOfLeg.targetChargeInkWh) / maxChargeKWH,\n }),\n ...(chargingInformationAtEndOfLeg.chargingParkPowerInkW && {\n chargingParkSpeed: toChargingSpeed(chargingInformationAtEndOfLeg.chargingParkPowerInkW),\n }),\n },\n };\n};\n\nconst parseSummary = (apiSummary: SummaryAPI, params: CalculateRouteParams): RouteSummary | LegSummary => {\n const maxChargeKWH = params?.vehicle && maxChargeKWHOf(params.vehicle);\n return {\n lengthInMeters: apiSummary.lengthInMeters,\n travelTimeInSeconds: apiSummary.travelDurationInSeconds,\n trafficDelayInSeconds: apiSummary.trafficDelayDurationInSeconds ?? 0,\n trafficLengthInMeters: apiSummary.trafficLengthInMeters ?? 0,\n historicTrafficTravelTimeInSeconds: apiSummary.historicTrafficTravelTimeInSeconds,\n liveTrafficIncidentsTravelTimeInSeconds: apiSummary.liveTrafficIncidentsTravelTimeInSeconds,\n noTrafficTravelTimeInSeconds: apiSummary.noTrafficTravelTimeInSeconds,\n departureTime: new Date(apiSummary.departureDateTime),\n arrivalTime: new Date(apiSummary.arrivalDateTime),\n deviationDistanceInMeters: apiSummary.deviationDistanceInMeters,\n deviationTimeInSeconds: apiSummary.deviationDurationInSeconds,\n ...(apiSummary.deviationPoint && {\n deviationPoint: apiSummary.deviationPoint.coordinates,\n }),\n fuelConsumptionInLiters: apiSummary.fuelConsumptionInLiters,\n totalChargingTimeInSeconds: apiSummary.totalChargingTimeInSeconds,\n batteryConsumptionInkWh: apiSummary.batteryConsumptionInKilowattHours ?? apiSummary.batteryConsumptionInkWh,\n ...(maxChargeKWH &&\n (() => {\n const batteryConsumption =\n apiSummary.batteryConsumptionInKilowattHours ?? apiSummary.batteryConsumptionInkWh;\n return batteryConsumption\n ? { batteryConsumptionInPCT: (100 * batteryConsumption) / maxChargeKWH }\n : undefined;\n })()),\n remainingChargeAtArrivalInkWh: apiSummary.remainingChargeAtArrivalInkWh,\n ...(maxChargeKWH &&\n apiSummary.remainingChargeAtArrivalInkWh && {\n remainingChargeAtArrivalInPCT: (100 * apiSummary.remainingChargeAtArrivalInkWh) / maxChargeKWH,\n }),\n ...(apiSummary.chargingInformationAtEndOfLeg && {\n chargingInformationAtEndOfLeg: toChargingStop(apiSummary.chargingInformationAtEndOfLeg, maxChargeKWH),\n }),\n };\n};\n\nconst parseRoutePath = (apiRouteLegs: LegAPI[]): LineString => ({\n type: 'LineString',\n coordinates: apiRouteLegs.flatMap((leg) => leg.path.coordinates),\n});\n\n// A stop is only visible as the gap between arriving on one leg and departing on the next, so it\n// takes the pair to see it. Everything spent there widens that one gap -- a requested wait, or\n// charging on an EV route, or both at one stop -- so the gap is reported as it is, and\n// `chargingInformationAtEndOfLeg` is left to say how much of it was charging.\nconst parseLegStopTime = (leg: LegAPI, nextLeg: LegAPI | undefined): number | undefined => {\n if (!nextLeg) return undefined;\n\n const arrival = new Date(leg.summary.arrivalDateTime).getTime();\n const nextDeparture = new Date(nextLeg.summary.departureDateTime).getTime();\n if (Number.isNaN(arrival) || Number.isNaN(nextDeparture)) return undefined;\n\n const stopSeconds = Math.round((nextDeparture - arrival) / 1000);\n return stopSeconds > 0 ? stopSeconds : undefined;\n};\n\nconst parseLegSectionProps = (apiLegs: LegAPI[], params: CalculateRouteParams): LegSectionProps[] =>\n apiLegs.reduce<LegSectionProps[]>((accumulator, leg, legIndex) => {\n const legCoordinateCount = leg.path.coordinates.length;\n const lastEndIndex = legIndex === 0 ? 0 : accumulator[legIndex - 1]?.endPointIndex;\n let endPointIndex: number | undefined;\n if (!isNil(lastEndIndex)) {\n endPointIndex =\n lastEndIndex === 0 ? Math.max(legCoordinateCount - 1, 0) : lastEndIndex + legCoordinateCount;\n }\n const stopTimeInSeconds = parseLegStopTime(leg, apiLegs[legIndex + 1]);\n // A leg is not reliably the one arriving at `locations[legIndex + 1]`, because the service\n // inserts charging stops of its own. It says which requested waypoint each leg ended at, so\n // that is what the caller gets to map a leg back with.\n const { originalWaypointIndexAtEndOfLeg } = leg.summary;\n accumulator.push({\n ...(!isNil(lastEndIndex) && { startPointIndex: lastEndIndex }),\n ...(endPointIndex !== undefined && { endPointIndex }),\n ...(!isNil(originalWaypointIndexAtEndOfLeg) && {\n originalWaypointIndex: originalWaypointIndexAtEndOfLeg,\n }),\n summary: {\n ...(parseSummary(leg.summary, params) as LegSummary),\n ...(stopTimeInSeconds !== undefined && { stopTimeInSeconds }),\n },\n id: generateId(),\n });\n return accumulator;\n }, []);\n\nconst toSectionProps = (section: BasicSectionAPI): SectionProps => ({\n id: generateId(),\n startPointIndex: section.startPathIndex,\n endPointIndex: section.endPathIndex,\n});\n\n/**\n * Map a traffic section to the SDK's semantic traffic incident categories.\n * Uses TEC main cause codes when available, falling back to the API's coarser `iconCategory`.\n * @ignore\n */\nexport const toTrafficCategories = (section: TrafficSectionAPI): TrafficIncidentCategory[] => {\n if (section.tec?.causes?.length) {\n return section.tec.causes.map((cause) => {\n switch (cause.mainCauseCode) {\n case 1:\n return 'jam';\n case 2:\n return 'accident';\n case 3:\n return 'roadworks';\n case 4:\n return 'narrow-lanes';\n case 5:\n return 'road-closed';\n case 9:\n return 'danger';\n case 11:\n return 'animals-on-road';\n case 13:\n return 'broken-down-vehicle';\n case 16:\n return 'lane-closed';\n case 17:\n return 'wind';\n case 18:\n return 'fog';\n case 19:\n return 'rain';\n case 22:\n return 'frost';\n case 23:\n return 'flooding';\n default:\n return 'other';\n }\n });\n }\n return [toTrafficIncidentCategory(section.iconCategory)];\n};\n\ntype BasicSectionKey =\n | 'carTrain'\n | 'ferry'\n | 'tunnel'\n | 'motorway'\n | 'pedestrian'\n | 'toll'\n | 'tollRoad'\n | 'carpool'\n | 'urban'\n | 'unpaved'\n | 'lowEmissionZone';\n\nconst parseSpeedLimitSections = (speedLimitSections: SpeedLimitSectionAPI[]): SpeedLimitSectionProps[] =>\n // Speeds arrive under `speedRestrictions`; the SDK exposes only the maximum limit.\n speedLimitSections\n .map((speedLimit): SpeedLimitSectionProps | undefined => {\n const max = speedLimit.speedRestrictions?.find((r) => r.type === 'maximum')?.inKilometersPerHour;\n return isNil(max) ? undefined : { ...toSectionProps(speedLimit), maxSpeedLimitInKmh: max };\n })\n .filter((speedLimit): speedLimit is SpeedLimitSectionProps => !!speedLimit);\n\nconst parseLaneSections = (laneSections: LanesSectionAPI[]): LaneSectionProps[] =>\n // Lane directions/separators are camelCase strings; map to the SDK's UPPER_SNAKE enums.\n laneSections.map(\n (lanesSection): LaneSectionProps => ({\n ...toSectionProps(lanesSection),\n lanes: (lanesSection.lanes ?? []).map(\n (lane): LaneDirection => ({\n directions: lane.directions.map(\n (direction) => LANE_DIRECTION_MAP[direction] ?? (direction as PossibleLaneDirection),\n ),\n ...(lane.follow && {\n follow: LANE_DIRECTION_MAP[lane.follow] ?? (lane.follow as PossibleLaneDirection),\n }),\n }),\n ),\n laneSeparators: (lanesSection.laneSeparators ?? []).map(\n (separator) => LANE_SEPARATOR_MAP[separator] ?? (separator as PossibleLaneSeparator),\n ),\n properties: lanesSection.properties,\n }),\n );\n\nconst parseImportantRoadStretchSections = (\n importantRoadStretchSections: ImportantRoadStretchSectionAPI[],\n): ImportantRoadStretchProps[] =>\n importantRoadStretchSections.map(\n (importantRoadStretch): ImportantRoadStretchProps => ({\n ...toSectionProps(importantRoadStretch),\n index: importantRoadStretch.importantRoadStretchIndex,\n ...(importantRoadStretch.streetName && { streetName: importantRoadStretch.streetName.text }),\n ...(importantRoadStretch.roadNumbers?.length && {\n roadNumbers: importantRoadStretch.roadNumbers.map((roadNumber) => roadNumber.text),\n }),\n }),\n );\n\n// The API always returns every section type when sections are requested. Restore the legacy filtering by\n// dropping the section keys the caller did not ask for (the always-present `leg` is kept).\nconst applyRequestedSectionFilter = (\n result: SectionsProps,\n sectionTypes: CalculateRouteParams['sectionTypes'],\n): void => {\n if (!sectionTypes) return;\n const requested = new Set<string>(sectionTypes);\n for (const key of Object.keys(result) as (keyof SectionsProps)[]) {\n if (key !== 'leg' && !requested.has(key)) {\n delete result[key];\n }\n }\n};\n\nconst parseSections = (apiRoute: RouteAPI, params: CalculateRouteParams): SectionsProps => {\n const result: SectionsProps = {\n leg: parseLegSectionProps(apiRoute.legs, params),\n };\n\n const sections = apiRoute.sections;\n if (!sections) return result;\n\n const addBasic = (key: BasicSectionKey, items?: BasicSectionAPI[]) => {\n if (items?.length) result[key] = items.map(toSectionProps);\n };\n\n addBasic('carTrain', sections.carTrain);\n addBasic('ferry', sections.ferry);\n addBasic('tunnel', sections.tunnel);\n addBasic('motorway', sections.motorway);\n addBasic('pedestrian', sections.pedestrian);\n addBasic('toll', sections.toll);\n addBasic('tollRoad', sections.tollRoad);\n addBasic('carpool', sections.carpool);\n addBasic('urban', sections.urban);\n addBasic('unpaved', sections.unpaved);\n addBasic('lowEmissionZone', sections.lowEmissionZone);\n\n if (sections.country?.length) {\n result.country = sections.country.map((country: CountrySectionAPI) => ({\n ...toSectionProps(country),\n countryCodeISO2: country.countryCodeIso2 ?? '',\n countryCodeISO3: toIso3(country.countryCodeIso2 ?? ''),\n }));\n }\n\n if (sections.travelMode?.length) {\n result.vehicleRestricted = sections.travelMode\n .filter((travelModeSection: TravelModeSectionAPI) => travelModeSection.travelMode === 'other')\n .map(toSectionProps);\n }\n\n if (sections.traffic?.length) {\n result.traffic = sections.traffic.map(\n (traffic: TrafficSectionAPI): TrafficSectionProps => ({\n ...toSectionProps(traffic),\n delayInSeconds: traffic.delayDurationInSeconds,\n effectiveSpeedInKmh: traffic.effectiveSpeedInKilometersPerHour,\n categories: toTrafficCategories(traffic),\n magnitudeOfDelay: toDelayMagnitude(traffic.delayMagnitude),\n tec: traffic.tec as TrafficIncidentTEC,\n ...(traffic.eventId && { eventId: traffic.eventId }),\n }),\n );\n }\n\n if (sections.speedLimit?.length) {\n result.speedLimit = parseSpeedLimitSections(sections.speedLimit);\n }\n\n if (sections.lanes?.length) {\n result.lanes = parseLaneSections(sections.lanes);\n }\n\n if (sections.roadShields?.length) {\n result.roadShields = sections.roadShields.map(\n (roadShield): RoadShieldSectionProps => ({\n ...toSectionProps(roadShield),\n roadShieldReferences: roadShield.roadShieldReferences as RoadShieldReference[],\n }),\n );\n }\n\n if (sections.tollVignette?.length) {\n result.tollVignette = sections.tollVignette.map(\n (tollVignette: CountrySectionAPI): CountrySectionProps => ({\n ...toSectionProps(tollVignette),\n countryCodeISO2: tollVignette.countryCodeIso2 ?? '',\n countryCodeISO3: toIso3(tollVignette.countryCodeIso2 ?? ''),\n }),\n );\n }\n\n if (sections.importantRoadStretch?.length) {\n result.importantRoadStretch = parseImportantRoadStretchSections(sections.importantRoadStretch);\n }\n\n applyRequestedSectionFilter(result, params.sectionTypes);\n\n return result;\n};\n\nconst DELTA = 0.0001;\nconst similar = (pointA: Position, pointB: Position): boolean =>\n Math.abs(pointA[0] - pointB[0]) < DELTA && Math.abs(pointA[1] - pointB[1]) < DELTA;\n\n// Maneuver camelCase JSON values → SDK UPPER_SNAKE_CASE Maneuver type.\n// The two renamed cases: continueStraight → STRAIGHT, passTollgate → TOLLGATE.\nconst MANEUVER_MAP: Record<string, Maneuver> = {\n depart: 'DEPART',\n waypointLeft: 'WAYPOINT_LEFT',\n waypointRight: 'WAYPOINT_RIGHT',\n waypointReached: 'WAYPOINT_REACHED',\n waypointAhead: 'WAYPOINT_AHEAD',\n arrive: 'ARRIVE',\n arriveLeft: 'ARRIVE_LEFT',\n arriveRight: 'ARRIVE_RIGHT',\n arriveAhead: 'ARRIVE_AHEAD',\n continueStraight: 'STRAIGHT',\n keepLeft: 'KEEP_LEFT',\n keepRight: 'KEEP_RIGHT',\n turnSlightRight: 'SLIGHT_RIGHT',\n turnRight: 'TURN_RIGHT',\n turnSharpRight: 'SHARP_RIGHT',\n turnSlightLeft: 'SLIGHT_LEFT',\n turnLeft: 'TURN_LEFT',\n turnSharpLeft: 'SHARP_LEFT',\n makeUTurn: 'MAKE_UTURN',\n roundaboutStraight: 'ROUNDABOUT_STRAIGHT',\n roundaboutSharpRight: 'ROUNDABOUT_SHARP_RIGHT',\n roundaboutRight: 'ROUNDABOUT_RIGHT',\n roundaboutSlightRight: 'ROUNDABOUT_SLIGHT_RIGHT',\n roundaboutSlightLeft: 'ROUNDABOUT_SLIGHT_LEFT',\n roundaboutLeft: 'ROUNDABOUT_LEFT',\n roundaboutSharpLeft: 'ROUNDABOUT_SHARP_LEFT',\n roundaboutBack: 'ROUNDABOUT_BACK',\n exitRoundabout: 'EXIT_ROUNDABOUT',\n exitMotorwayLeft: 'EXIT_MOTORWAY_LEFT',\n exitMotorwayRight: 'EXIT_MOTORWAY_RIGHT',\n switchMotorwayLeft: 'SWITCH_MOTORWAY_LEFT',\n switchMotorwayRight: 'SWITCH_MOTORWAY_RIGHT',\n enterCarpoolLaneLeft: 'ENTER_CARPOOL_LANE_LEFT',\n enterCarpoolLaneRight: 'ENTER_CARPOOL_LANE_RIGHT',\n exitCarpoolLaneLeft: 'EXIT_CARPOOL_LANE_LEFT',\n exitCarpoolLaneRight: 'EXIT_CARPOOL_LANE_RIGHT',\n mergeLeftLane: 'MERGE_LEFT_LANE',\n mergeRightLane: 'MERGE_RIGHT_LANE',\n takeShipFerry: 'TAKE_SHIP_FERRY',\n takeCarTrain: 'TAKE_CAR_TRAIN',\n leaveShipFerry: 'LEAVE_SHIP_FERRY',\n leaveCarTrain: 'LEAVE_CAR_TRAIN',\n crossBorder: 'CROSS_BORDER',\n passTollgate: 'TOLLGATE',\n};\n\nconst ROAD_PROPERTY_MAP: Record<string, RoadInformationProperty> = {\n urban: 'URBAN',\n motorway: 'MOTORWAY',\n controlledAccess: 'CONTROLLED_ACCESS',\n};\n\n// Landmark camelCase JSON values → SDK UPPER_SNAKE_CASE Landmark type.\nconst LANDMARK_MAP: Record<string, Landmark> = {\n endOfRoad: 'END_OF_ROAD',\n atTrafficLight: 'AT_TRAFFIC_LIGHT',\n onToBridge: 'ON_TO_BRIDGE',\n onBridge: 'ON_BRIDGE',\n afterBridge: 'AFTER_BRIDGE',\n intoTunnel: 'INTO_TUNNEL',\n insideTunnel: 'INSIDE_TUNNEL',\n afterTunnel: 'AFTER_TUNNEL',\n};\n\n// Toll payment camelCase JSON values → SDK UPPER_SNAKE_CASE TollPaymentType.\nconst TOLL_PAYMENT_MAP: Record<string, TollPaymentType> = {\n cashCoinsAndBills: 'CASH_COINS_AND_BILLS',\n cashBillsOnly: 'CASH_BILLS_ONLY',\n cashCoinsOnly: 'CASH_COINS_ONLY',\n cashExactChange: 'CASH_EXACT_CHANGE',\n creditCard: 'CREDIT_CARD',\n debitCard: 'DEBIT_CARD',\n travelCard: 'TRAVEL_CARD',\n etc: 'ETC',\n etcTransponder: 'ETC_TRANSPONDER',\n etcVideoCamera: 'ETC_VIDEO_CAMERA',\n subscription: 'SUBSCRIPTION',\n};\n\n// Lane direction camelCase JSON values → SDK UPPER_SNAKE_CASE PossibleLaneDirection.\nconst LANE_DIRECTION_MAP: Record<string, PossibleLaneDirection> = {\n straight: 'STRAIGHT',\n slightRight: 'SLIGHT_RIGHT',\n right: 'RIGHT',\n sharpRight: 'SHARP_RIGHT',\n rightUTurn: 'RIGHT_U_TURN',\n slightLeft: 'SLIGHT_LEFT',\n left: 'LEFT',\n sharpLeft: 'SHARP_LEFT',\n leftUTurn: 'LEFT_U_TURN',\n};\n\n// Lane separator camelCase JSON values → SDK UPPER_SNAKE_CASE PossibleLaneSeparator.\nconst LANE_SEPARATOR_MAP: Record<string, PossibleLaneSeparator> = {\n unknown: 'UNKNOWN',\n noMarking: 'NO_MARKING',\n longDashed: 'LONG_DASHED',\n doubleSolid: 'DOUBLE_SOLID',\n singleSolid: 'SINGLE_SOLID',\n solidDashed: 'SOLID_DASHED',\n dashedSolid: 'DASHED_SOLID',\n shortDashed: 'SHORT_DASHED',\n shadedAreaMarking: 'SHADED_AREA_MARKING',\n dashedBlocks: 'DASHED_BLOCKS',\n doubleDashed: 'DOUBLE_DASHED',\n crossingAlert: 'CROSSING_ALERT',\n physicalDivider: 'PHYSICAL_DIVIDER',\n physicalDividerLessThan3m: 'PHYSICAL_DIVIDER_LESS_THAN_3M',\n physicalDividerGuardrail: 'PHYSICAL_DIVIDER_GUARDRAIL',\n curb: 'CURB',\n};\n\n// Maneuver-view angles are relative-direction words, not degrees → SDK ManeuverAngle.\nconst MANEUVER_ANGLE_MAP: Record<string, ManeuverAngle> = {\n straight: 'STRAIGHT',\n slightRight: 'SLIGHT_RIGHT',\n right: 'RIGHT',\n sharpRight: 'SHARP_RIGHT',\n slightLeft: 'SLIGHT_LEFT',\n left: 'LEFT',\n sharpLeft: 'SHARP_LEFT',\n back: 'BACK',\n};\n\n// Roundabout kinds → SDK RoundaboutType.\nconst ROUNDABOUT_TYPE_MAP: Record<string, RoundaboutType> = {\n regular: 'REGULAR',\n small: 'SMALL',\n};\n\n// Side-road sides are lowercase on the wire → SDK SideRoadSide.\nconst SIDE_ROAD_SIDE_MAP: Record<string, SideRoadSide> = {\n left: 'LEFT',\n right: 'RIGHT',\n leftAndRight: 'LEFT_AND_RIGHT',\n};\n\nconst EMPTY_ROAD_INFO: RoadInformation = { properties: [] };\n\nconst toInstructionText = (instructionText: InstructionTextAPI): InstructionText => ({ text: instructionText.text });\n\nconst parseInstructionRoadInformation = (road: InstructionRoadInformationAPI): RoadInformation => ({\n properties: (road.properties ?? []).map(\n (p: string) => ROAD_PROPERTY_MAP[p] ?? (p.toUpperCase() as RoadInformationProperty),\n ),\n ...(road.roadNames?.length && { streetName: toInstructionText(road.roadNames[0].identifier) }),\n ...(road.countryCodeIso2 && { countryCode: toIso3(road.countryCodeIso2) }),\n ...(road.roadShields?.length && {\n roadShields: road.roadShields.map(\n (s: InstructionRoadShieldAPI): RoadShield => ({\n roadNumber: s.roadNumber ? toInstructionText(s.roadNumber) : { text: '' },\n countryCode: toIso3(s.countryCodeIso2 ?? ''),\n ...(s.countrySubdivisionCodeIso && { stateCode: s.countrySubdivisionCodeIso }),\n ...(s.iconReference && { roadShieldReference: s.iconReference }),\n }),\n ),\n }),\n});\n\nconst parseSignpost = (signpost: NonNullable<InstructionAPI['signpost']>): Signpost | undefined => {\n if (!signpost.exitName && !signpost.exitNumber && !signpost.towardName) return undefined;\n return {\n ...(signpost.exitName && { exitName: toInstructionText(signpost.exitName) }),\n ...(signpost.exitNumber && { exitNumber: toInstructionText(signpost.exitNumber) }),\n // towardName is required on the SDK type; default to empty text when the API omits it.\n towardName: signpost.towardName ? toInstructionText(signpost.towardName) : { text: '' },\n };\n};\n\n// Tollgate + border-crossing fields, only present on the relevant maneuvers (kept separate to keep\n// the main instruction builder's complexity in check).\nconst parseTollAndCrossing = (a: InstructionAPI): Partial<Instruction> => ({\n ...(a.tollgateName && { tollgateName: toInstructionText(a.tollgateName) }),\n ...(a.tollPaymentTypes?.length && {\n tollPaymentTypes: a.tollPaymentTypes.map((t) => TOLL_PAYMENT_MAP[t] ?? (t as TollPaymentType)),\n }),\n ...(a.countryCrossingFromName && { countryCrossingFromName: toInstructionText(a.countryCrossingFromName) }),\n ...(a.countryCrossingFromCodeIso2 && { countryCrossingFromCode: toIso3(a.countryCrossingFromCodeIso2) }),\n ...(a.countryCrossingToName && { countryCrossingToName: toInstructionText(a.countryCrossingToName) }),\n ...(a.countryCrossingToCodeIso2 && { countryCrossingToCode: toIso3(a.countryCrossingToCodeIso2) }),\n});\n\n// Optional maneuver detail fields, only present on the relevant maneuver kinds.\nconst toManeuverAngle = (angle: string): ManeuverAngle => MANEUVER_ANGLE_MAP[angle] ?? (angle as ManeuverAngle);\n\n// The API side is lowercase and carries `isDrivable`; both need mapping onto the SDK's SideRoad.\nconst parseSideRoads = (sideRoads: SideRoadAPI[]): SideRoad[] =>\n sideRoads.map((sideRoad) => ({\n side: SIDE_ROAD_SIDE_MAP[sideRoad.side] ?? (sideRoad.side as SideRoadSide),\n offsetFromManeuverInMeters: sideRoad.offsetFromManeuverInMeters,\n ...(!isNil(sideRoad.isDrivable) && { isDrivable: sideRoad.isDrivable }),\n }));\n\nconst parseManeuverView = (view: ManeuverViewAPI): ManeuverView => ({\n ...(view.onRouteAngle && { onRouteAngle: toManeuverAngle(view.onRouteAngle) }),\n offRouteAngles: (view.offRouteAngles ?? []).map(toManeuverAngle),\n});\n\nconst parseManeuverExtras = (a: InstructionAPI): Partial<Instruction> => ({\n ...(a.message && { message: a.message }),\n ...(a.intersectionName && { intersectionName: toInstructionText(a.intersectionName) }),\n ...(a.drivingSide && { drivingSide: a.drivingSide.toUpperCase() as DrivingSide }),\n ...(a.landmark && { landmark: LANDMARK_MAP[a.landmark] ?? (a.landmark as Landmark) }),\n ...(!isNil(a.distanceToPreviousTrafficLightInMeters) && {\n trafficLightOffsetInMeters: a.distanceToPreviousTrafficLightInMeters,\n }),\n ...(a.sideRoads && { sideRoads: parseSideRoads(a.sideRoads) }),\n ...(a.maneuverView && { maneuverView: parseManeuverView(a.maneuverView) }),\n ...(a.roundaboutType && {\n roundaboutType: ROUNDABOUT_TYPE_MAP[a.roundaboutType] ?? (a.roundaboutType as RoundaboutType),\n }),\n ...(!isNil(a.isManeuverObligatory) && { isManeuverObligatory: a.isManeuverObligatory }),\n ...(!isNil(a.changeOfAngleInDegrees) && { changeOfAngleInDegrees: a.changeOfAngleInDegrees }),\n ...(!isNil(a.ambiguousExitOffsetFromManeuverInMeters) && {\n offsetOfAmbiguousExitFromManeuverInMeters: a.ambiguousExitOffsetFromManeuverInMeters,\n }),\n ...(!isNil(a.roundaboutExitNumber) && { roundaboutExitNumber: a.roundaboutExitNumber }),\n});\n\n// Road-shield related fields: signpost, the atlas base URL, and the flat reference lists. V2 exposed\n// a flat per-instruction road-shield list; the API carries them under nextRoadInformation.\nconst parseRoadShieldFields = (a: InstructionAPI, roadShieldAtlasReference?: string): Partial<Instruction> => {\n const signpost = a.signpost ? parseSignpost(a.signpost) : undefined;\n const roadShieldReferences = (a.nextRoadInformation?.roadShields ?? [])\n .map((s) => s.iconReference)\n .filter((ref): ref is RoadShieldReference => !!ref);\n return {\n ...(signpost && { signpost }),\n ...(roadShieldAtlasReference && { roadShieldAtlasReference }),\n ...(roadShieldReferences.length && { roadShieldReferences }),\n ...(a.signpost?.exitIconReference && {\n signpostRoadShieldReferences: [a.signpost.exitIconReference],\n }),\n };\n};\n\nconst parseInstruction = (\n a: InstructionAPI,\n maneuverPoint: [number, number],\n pathPointIndex: number,\n roadShieldAtlasReference?: string,\n): Instruction => ({\n maneuverPoint,\n pathPointIndex,\n routeOffsetInMeters: a.routeOffsetInMeters,\n maneuver: (a.maneuver ? (MANEUVER_MAP[a.maneuver] ?? a.maneuver) : a.maneuver) as Maneuver,\n routePath: (a.routePath ?? []).map((rp) => ({\n point: rp.point.coordinates,\n distanceInMeters: rp.distanceFromRouteStartInMeters,\n travelTimeInSeconds: rp.travelTimeFromRouteStartInSeconds,\n })),\n nextRoadInfo: a.nextRoadInformation ? parseInstructionRoadInformation(a.nextRoadInformation) : EMPTY_ROAD_INFO,\n previousRoadInfo: a.previousRoadInformation\n ? parseInstructionRoadInformation(a.previousRoadInformation)\n : EMPTY_ROAD_INFO,\n ...parseManeuverExtras(a),\n ...parseRoadShieldFields(a, roadShieldAtlasReference),\n ...parseTollAndCrossing(a),\n});\n\nconst parseGuidance = (\n instructions: InstructionAPI[],\n path: Position[],\n roadShieldAtlasReference?: string,\n): Guidance => {\n const parsed: Instruction[] = [];\n let lastPathIndex = 0;\n\n for (const apiInstruction of instructions) {\n const maneuverPoint = apiInstruction.maneuverPoint.coordinates;\n\n for (let pathIndex = lastPathIndex; pathIndex < path.length; pathIndex++) {\n if (similar(path[pathIndex], maneuverPoint)) {\n lastPathIndex = pathIndex;\n break;\n }\n }\n\n parsed.push(parseInstruction(apiInstruction, maneuverPoint, lastPathIndex, roadShieldAtlasReference));\n }\n\n return { instructions: parsed };\n};\n\nconst parseRoute = (\n apiRoute: RouteAPI,\n index: number,\n params: CalculateRouteParams,\n roadShieldAtlasReference?: string,\n): Route => {\n const geometry = parseRoutePath(apiRoute.legs);\n return {\n type: 'Feature',\n geometry,\n id: generateId(),\n bbox: bboxFromGeoJSON(geometry) as BBox,\n properties: {\n index,\n summary: parseSummary(apiRoute.summary, params),\n sections: parseSections(apiRoute, params),\n ...(apiRoute.instructions?.length && {\n guidance: parseGuidance(apiRoute.instructions, geometry.coordinates, roadShieldAtlasReference),\n }),\n ...(apiRoute.progressPoints && {\n progress: apiRoute.progressPoints.map((progressPoint: ProgressPointAPI) => ({\n pointIndex: progressPoint.pathIndex,\n distanceInMeters: progressPoint.distanceInMeters,\n travelTimeInSeconds: progressPoint.travelDurationInSeconds,\n })),\n }),\n },\n };\n};\n\n/**\n * Default method for parsing calculate route response from {@link CalculateRouteResponseAPI}\n * @param apiResponse The Routing API response.\n * @param params The params used to calculate this route.\n */\nexport const parseCalculateRouteResponse = (\n apiResponse: CalculateRouteResponseAPI,\n params: CalculateRouteParams,\n): Routes => {\n const features = apiResponse.routes.map((apiRoute, index) =>\n parseRoute(apiRoute, index, params, apiResponse.roadShieldAtlasReference),\n );\n const bbox = bboxFromGeoJSON(features);\n return { type: 'FeatureCollection', ...(bbox && { bbox }), features };\n};\n","import type { Routes } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { routeRequestValidationConfig } from './calculateRouteRequestSchema';\nimport { buildCalculateRouteRequest } from './requestBuilder';\nimport { parseCalculateRouteResponse } from './responseParser';\nimport { parseRoutingResponseError } from './routingResponseErrorParser';\nimport type { CalculateRoutePOSTDataAPI } from './types/apiRequestTypes';\nimport type { CalculateRouteResponseAPI } from './types/apiResponseTypes';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\n/**\n * @ignore\n */\nexport type CalculateRouteTemplate = ServiceTemplate<\n CalculateRouteParams,\n FetchInput<CalculateRoutePOSTDataAPI>,\n CalculateRouteResponseAPI,\n Routes\n>;\n\n/**\n * @ignore\n */\nexport const calculateRouteTemplate: CalculateRouteTemplate = {\n requestValidation: routeRequestValidationConfig,\n buildRequest: buildCalculateRouteRequest,\n sendRequest: fetchWith,\n parseResponse: parseCalculateRouteResponse,\n parseResponseError: parseRoutingResponseError,\n apiVersion: 3,\n};\n","import type { Routes } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { calculateRouteTemplate } from './calculateRouteTemplate';\nimport type { CalculateRouteParams } from './types/calculateRouteParams';\n\n/**\n * Calculates a route between an origin and destination, optionally passing through extra waypoints.\n *\n * @param params - Route calculation parameters including locations, travel mode, and other options\n * @returns Promise resolving to calculated route(s) with geometry, distance, and travel time\n *\n * @see [Calculate Route API Documentation](https://docs.tomtom.com/routing-api/documentation/tomtom-maps/calculate-route)\n *\n * @group Routing\n */\nexport const calculateRoute = async (params: CalculateRouteParams): Promise<Routes> =>\n callService(params, calculateRouteTemplate, 'Routing');\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { commonGeocodeAndFuzzySearchParamsSchema } from '../shared/schema/commonGeocodeAndFuzzySearchParamsSchema';\nimport { searchFiltersSchema } from '../shared/schema/searchFiltersSchema';\n\nconst fuzzySearchRequestOptional = z.object({\n minFuzzyLevel: z.number().min(1).max(4).optional(),\n maxFuzzyLevel: z.number().min(1).max(4).optional(),\n // Both parents declare `filters` and `.extend()` replaces rather than merges, so set both halves.\n filters: searchFiltersSchema.optional(),\n});\n\n/**\n * @ignore\n */\nexport const fuzzySearchRequestSchema = commonSearchParamsSchema.extend(\n commonGeocodeAndFuzzySearchParamsSchema.extend(fuzzySearchRequestOptional.shape).shape,\n);\n","import type { GetObject } from '../shared';\nimport { appendCommonSearchParams } from '../shared/request/commonSearchRequestBuilder';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport {\n appendByJoiningParamValue,\n appendGeoBiasParams,\n appendOptionalParam,\n buildPlacesRequestHeaders,\n} from '../shared/request/requestBuildingUtils';\nimport { appendSearchCursorParam } from '../shared/searchCursor';\nimport type { FuzzySearchParams } from './types';\n\nconst buildUrlBasePath = (params: FuzzySearchParams): string =>\n resolvePlacesEndpointUrl(params, `search/${encodeURIComponent(params.query ?? '')}.json`);\n\n/**\n * Default function for building a fuzzy search request from {@link FuzzySearchParams}\n * @param params The fuzzy search parameters, with global configuration already merged into them.\n */\nexport const buildFuzzySearchRequest = (params: FuzzySearchParams): GetObject => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n appendCommonSearchParams(url, params);\n const urlParams = url.searchParams;\n appendOptionalParam(urlParams, 'typeahead', params.typeahead);\n appendSearchCursorParam(urlParams, params.cursor, 'FuzzySearch');\n appendByJoiningParamValue(urlParams, 'countrySet', params.filters?.countries);\n appendGeoBiasParams(urlParams, params.geoBias);\n appendOptionalParam(urlParams, 'minFuzzyLevel', params.minFuzzyLevel);\n appendOptionalParam(urlParams, 'maxFuzzyLevel', params.maxFuzzyLevel);\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { latLonAPIToPosition } from '../shared/geometry';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { FuzzySearchResponse, FuzzySearchResponseAPI, QueryIntent, QueryIntentAPI } from './types';\n\nconst queryIntentApiToSdk = (intentApi: QueryIntentAPI): QueryIntent => {\n let intent;\n switch (intentApi.type) {\n case 'COORDINATE':\n intent = { ...intentApi, details: { position: latLonAPIToPosition(intentApi.details) } };\n break;\n case 'NEARBY':\n intent = {\n ...intentApi,\n details: {\n position: latLonAPIToPosition({ lon: intentApi.details.lon, lat: intentApi.details.lat }),\n text: intentApi.details.text,\n query: intentApi.details.query,\n },\n };\n break;\n case 'BOOKMARK':\n case 'W3W':\n intent = intentApi;\n }\n return intent;\n};\n\n/**\n * Default function to parse a fuzzy search response.\n * @param apiResponse The API response.\n */\nexport const parseFuzzySearchResponse = (apiResponse: FuzzySearchResponseAPI): FuzzySearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n const queryIntent = apiResponse.summary.queryIntent?.map(queryIntentApiToSdk);\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary, { paginated: true }),\n ...(queryIntent && { queryIntent }),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { fuzzySearchRequestSchema } from './fuzzySearchRequestSchema';\nimport { buildFuzzySearchRequest } from './requestBuilder';\nimport { parseFuzzySearchResponse } from './responseParser';\nimport type { FuzzySearchParams, FuzzySearchResponse, FuzzySearchResponseAPI } from './types';\n\n/**\n * Fuzzy search service template type.\n * @ignore\n */\nexport type FuzzySearchTemplate = ServiceTemplate<\n FuzzySearchParams,\n GetObject,\n FuzzySearchResponseAPI,\n FuzzySearchResponse\n>;\n\n/**\n * Fuzzy search service template main implementation.\n * @ignore\n */\nexport const fuzzySearchTemplate: FuzzySearchTemplate = {\n requestValidation: { schema: fuzzySearchRequestSchema },\n buildRequest: buildFuzzySearchRequest,\n sendRequest: get,\n parseResponse: parseFuzzySearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport { fuzzySearchTemplate } from './fuzzySearchTemplate';\nimport type { FuzzySearchParams, FuzzySearchResponse } from './types';\n\n/**\n * Search for places using free-text queries with fuzzy matching.\n *\n * The Fuzzy Search service provides a flexible search that handles typos, abbreviations,\n * and incomplete addresses. It searches across POIs (Points of Interest), addresses,\n * and geographic areas to find the best matches for your query.\n *\n * @remarks\n * Key features:\n * - **Typo tolerance**: Handles misspellings and typing errors\n * - **Partial matching**: Works with incomplete queries\n * - **Multi-category search**: Searches POIs, addresses, and places simultaneously\n * - **Position bias**: Prioritizes results near a given location\n * - **Flexible input**: Accepts natural language queries\n *\n * The service is ideal for:\n * - User-facing search boxes where typos are common\n * - Location lookup without knowing exact names\n * - General \"find anything\" search functionality\n * - Autocomplete with final selection\n *\n * @param params Fuzzy search parameters including the search query\n *\n * @returns Promise resolving to a collection of matching places\n *\n * @example\n * ```typescript\n * // Basic search with typo tolerance\n * const results = await fuzzySearch({\n * query: 'amstrdam' // Typo: missing 'e'\n * });\n * // Still finds \"Amsterdam\"\n *\n * // Search near a specific location\n * const nearby = await fuzzySearch({\n * query: 'pizza',\n * geoBias: { position: [4.9041, 52.3676], radiusMeters: 2000 }, // Within 2km of Amsterdam\n * limit: 10\n * });\n *\n * // Search with category filter\n * const restaurants = await fuzzySearch({\n * filters: { poiCategories: ['ITALIAN_RESTAURANT'] },\n * geoBias: { position: [4.9041, 52.3676] }\n * });\n *\n * // Partial address search restricted to a country\n * const addresses = await fuzzySearch({\n * query: '123 main st',\n * filters: { countries: ['US'] },\n * limit: 5\n * });\n * ```\n *\n * @see [Fuzzy Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/fuzzy-search)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @ignore (exposed via 'search')\n */\nexport const fuzzySearch = async (params: FuzzySearchParams): Promise<FuzzySearchResponse> =>\n callService(params, fuzzySearchTemplate, 'FuzzySearch');\n","import { z } from 'zod';\nimport { commonSearchParamsSchema } from '../search/commonSearchParamsSchema';\nimport { featureCollectionSchema, geometrySchema } from '../shared/schema/geometriesSchema';\n\nconst geometrySearchRequestMandatory = z.object({\n geometries: z.array(z.union([featureCollectionSchema, geometrySchema])),\n});\n\n/**\n * @ignore\n */\nexport const geometrySearchRequestSchema = commonSearchParamsSchema.extend(geometrySearchRequestMandatory.shape);\n","import { bboxFromCoordsArray } from '@tomtom-org/maps-sdk/core';\nimport type { MultiPolygon, Position } from 'geojson';\nimport type { PostObject } from '../shared';\nimport { sampleWithinMaxLength } from '../shared/arrays';\nimport { positionToCSVLatLon } from '../shared/geometry';\nimport { appendCommonSearchParams } from '../shared/request/commonSearchRequestBuilder';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport { buildPlacesRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport type { GeometryAPI, GeometrySearchParams, GeometrySearchPayloadAPI, SearchGeometryInput } from './types';\n\nconst findFiftyLargestPolygons = (searchGeometry: MultiPolygon): Position[][][] => {\n // we calculate the size of each polygon based on bounding box (simplified)\n // we put it into a map for easy sorting\n // we sort the map by using size and keep only 50 largest polygons\n let polygonSizeMap = new Map<Position[][], number>();\n searchGeometry.coordinates.forEach((polygon) => {\n const bboxOfPolygon = bboxFromCoordsArray(polygon[0]);\n if (bboxOfPolygon) {\n const polygonSize = Math.abs((bboxOfPolygon[2] - bboxOfPolygon[0]) * (bboxOfPolygon[3] - bboxOfPolygon[1]));\n polygonSizeMap.set(polygon, polygonSize);\n }\n });\n polygonSizeMap = new Map([...polygonSizeMap.entries()].sort((a, b) => b[1] - a[1]).splice(0, 50));\n return [...polygonSizeMap.keys()];\n};\n\nconst sdkGeometryToApiGeometries = (searchGeometry: SearchGeometryInput): GeometryAPI[] => {\n switch (searchGeometry.type) {\n case 'Circle':\n return [\n {\n type: 'CIRCLE',\n radius: searchGeometry.radius,\n position: positionToCSVLatLon(searchGeometry.coordinates),\n },\n ];\n case 'Polygon':\n return [\n {\n type: 'POLYGON',\n vertices: sampleWithinMaxLength(searchGeometry.coordinates[0], 50).map((coord) =>\n positionToCSVLatLon(coord),\n ),\n },\n ];\n case 'MultiPolygon': {\n if (searchGeometry.coordinates.length > 50) {\n // we have too many polygons for the service to work\n return findFiftyLargestPolygons(searchGeometry).flatMap((polygonCoords) =>\n sdkGeometryToApiGeometries({ type: 'Polygon', coordinates: polygonCoords }),\n );\n }\n return searchGeometry.coordinates.flatMap((polygonCoords) =>\n sdkGeometryToApiGeometries({ type: 'Polygon', coordinates: polygonCoords }),\n );\n }\n case 'FeatureCollection':\n return searchGeometry.features.flatMap((feature) => sdkGeometryToApiGeometries(feature.geometry));\n default:\n // @ts-ignore\n throw new Error(`Type ${(searchGeometry as unknown).type} is not supported`);\n }\n};\n\nconst buildUrlBasePath = (mergedOptions: GeometrySearchParams): string =>\n resolvePlacesEndpointUrl(mergedOptions, `geometrySearch/${encodeURIComponent(mergedOptions.query ?? '')}.json`);\n\n/**\n * Default function for building a geometry search request from {@link GeometrySearchParams}\n * @param params The geometry search parameters, with global configuration already merged into them.\n */\nexport const buildGeometrySearchRequest = (params: GeometrySearchParams): PostObject<GeometrySearchPayloadAPI> => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n appendCommonSearchParams(url, params);\n\n return {\n url,\n headers: buildPlacesRequestHeaders(params),\n data: {\n geometryList: params.geometries.flatMap(sdkGeometryToApiGeometries),\n },\n };\n};\n","import { bboxFromGeoJSON, bboxOnlyIfWithArea } from '@tomtom-org/maps-sdk/core';\nimport { parseSearchAPIResult, parseSummaryAPI } from '../shared/searchResultParsing';\nimport type { GeometrySearchResponse, GeometrySearchResponseAPI } from './types';\n\n/**\n * Default function to parse a geometry search response.\n * @param apiResponse The API response.\n */\nexport const parseGeometrySearchResponse = (apiResponse: GeometrySearchResponseAPI): GeometrySearchResponse => {\n const features = apiResponse.results.map(parseSearchAPIResult);\n const bbox = bboxOnlyIfWithArea(bboxFromGeoJSON(features));\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseSummaryAPI(apiResponse.summary),\n },\n features,\n ...(bbox && { bbox }),\n };\n};\n","import type { ServiceTemplate } from '../shared';\nimport { post } from '../shared/fetch';\nimport { geometrySearchRequestSchema } from './geometrySearchRequestSchema';\nimport { buildGeometrySearchRequest } from './requestBuilder';\nimport { parseGeometrySearchResponse } from './responseParser';\nimport type {\n GeometrySearchParams,\n GeometrySearchRequestAPI,\n GeometrySearchResponse,\n GeometrySearchResponseAPI,\n} from './types';\n\n/**\n * Geometry search service template type.\n * @ignore\n */\nexport type GeometrySearchTemplate = ServiceTemplate<\n GeometrySearchParams,\n GeometrySearchRequestAPI,\n GeometrySearchResponseAPI,\n GeometrySearchResponse\n>;\n\n/**\n * Geometry search service template main implementation.\n * @ignore\n */\nexport const geometrySearchTemplate: GeometrySearchTemplate = {\n requestValidation: { schema: geometrySearchRequestSchema },\n buildRequest: buildGeometrySearchRequest,\n sendRequest: post,\n parseResponse: parseGeometrySearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport { geometrySearchTemplate } from './geometrySearchTemplate';\nimport type { GeometrySearchParams, GeometrySearchResponse } from './types';\n\n/**\n * Search for places within specific geographic boundaries.\n *\n * The Geometry Search service finds places that fall within one or more defined areas,\n * such as polygons, circles, or corridors. This is essential for location-based filtering\n * and spatial queries where you need results constrained to specific regions.\n *\n * @remarks\n * Key features:\n * - **Area-based search**: Find places within polygons, circles, or along routes\n * - **Multiple geometries**: Search across several areas simultaneously\n * - **Precise boundaries**: Only returns results within the specified areas\n * - **Combined with text**: Filter by query text within the geometric boundaries\n * - **Category filtering**: Narrow results by POI categories\n *\n * Common use cases:\n * - **Route-based search**: \"Find gas stations along my route\"\n * - **Area filtering**: \"Restaurants within this neighborhood polygon\"\n * - **Corridor search**: \"Hotels within 5km of the highway\"\n * - **Service areas**: \"Stores within our delivery zone\"\n * - **Geofencing**: Places within administrative or custom boundaries\n *\n * @param params Geometry search parameters including geometries and optional query\n *\n * @returns Promise resolving to places within the specified geometries\n *\n * @example\n * ```typescript\n * // Search within a polygon (neighborhood boundaries)\n * const inArea = await geometrySearch({\n * query: 'coffee shop',\n * geometries: [{\n * type: 'Polygon',\n * coordinates: [[\n * [4.88, 52.36],\n * [4.90, 52.36],\n * [4.90, 52.38],\n * [4.88, 52.38],\n * [4.88, 52.36]\n * ]]\n * }]\n * });\n *\n * // Find POIs along a route (pass the route LineString as geometry)\n * const alongRoute = await geometrySearch({\n * query: 'gas station',\n * geometries: [routeLineString], // LineString from calculateRoute result\n * limit: 10\n * });\n *\n * // Search multiple areas at once\n * const multiArea = await geometrySearch({\n * query: 'pharmacy',\n * geometries: [polygonA, polygonB, polygonC],\n * limit: 20\n * });\n *\n * // Category search within geometry\n * const restaurants = await geometrySearch({\n * filters: { poiCategories: ['ITALIAN_RESTAURANT'] },\n * geometries: [cityBoundary]\n * });\n * ```\n *\n * @see [Geometry Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/geometry-search)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @ignore (exposed via 'search')\n */\nexport const geometrySearch = async (params: GeometrySearchParams): Promise<GeometrySearchResponse> =>\n callService(params, geometrySearchTemplate, 'GeometrySearch');\n","import type { Place, Places, SearchPlaceProps } from '@tomtom-org/maps-sdk/core';\nimport type { AlongRouteSearchParams } from '../along-route-search';\nimport { alongRouteSearch } from '../along-route-search/alongRouteSearch';\nimport type { FuzzySearchParams, QueryIntent } from '../fuzzy-search';\nimport { fuzzySearch } from '../fuzzy-search/fuzzySearch';\nimport type { GeometrySearchParams } from '../geometry-search';\nimport { geometrySearch } from '../geometry-search/geometrySearch';\nimport type { SearchSummary } from '../shared';\nimport { fetchFirstPlace } from '../shared/firstPlace';\n\ntype SearchFeatureCollectionProps = SearchSummary & {\n queryIntent?: QueryIntent[];\n};\n\n/** The keys of `Params` that `Base` lacks, each forbidden. */\ntype Without<Params, Base> = { [Key in Exclude<keyof Params, keyof Base>]?: never };\n\n/**\n * What {@link discoverPlaces} takes: one search variant's params, with the other variants' own\n * options forbidden. So `{ geometries, cursor }` is a compile error rather than a cursor silently\n * dropped, as it would be with a plain union.\n *\n * @group Places Search\n */\nexport type DiscoverPlacesParams =\n | (FuzzySearchParams &\n Without<GeometrySearchParams, FuzzySearchParams> &\n Without<AlongRouteSearchParams, FuzzySearchParams>)\n | (GeometrySearchParams &\n Without<FuzzySearchParams, GeometrySearchParams> &\n Without<AlongRouteSearchParams, GeometrySearchParams> & { filters?: { countries?: never } })\n | (AlongRouteSearchParams &\n Without<FuzzySearchParams, AlongRouteSearchParams> &\n Without<GeometrySearchParams, AlongRouteSearchParams> & { filters?: { countries?: never } });\n\n/**\n * Search service response containing places that match the query.\n *\n * Collection of place features with search-specific properties like relevance scores and distances.\n *\n * @group Places Search\n */\nexport type DiscoverPlacesResponse = Places<SearchPlaceProps, SearchFeatureCollectionProps>;\n\n/**\n * Universal search function for finding places by text query or within geometries.\n *\n * This is a unified interface that automatically routes to either:\n * - **Geometry Search**: When geometries parameter is provided (search within specific areas)\n * - **Fuzzy Search**: When no geometries provided (free-text search)\n *\n * @remarks\n * The search service provides:\n * - POI (Points of Interest) search\n * - Address search\n * - Geographic area search\n * - Category-based filtering\n * - Position-based relevance ranking\n *\n * Results are ranked by relevance with scores and optional distances.\n *\n * @param params - Search parameters: {@link FuzzySearchParams}, {@link GeometrySearchParams} or {@link AlongRouteSearchParams}\n *\n * @returns Promise resolving to a collection of matching places\n *\n * @example\n * ```typescript\n * // Free-text search near a location\n * const results = await discoverPlaces({\n * query: 'pizza restaurant',\n * geoBias: { position: [4.9041, 52.3676] }, // Amsterdam\n * limit: 10\n * });\n *\n * // Search within a specific area\n * const areaResults = await discoverPlaces({\n * query: 'coffee shop',\n * geometries: [polygon], // Search within this polygon\n * limit: 20\n * });\n *\n * // Category search\n * const restaurants = await discoverPlaces({\n * filters: { poiCategories: ['ITALIAN_RESTAURANT'] },\n * geoBias: { position: [4.9041, 52.3676], radiusMeters: 5000 } // Within 5km\n * });\n * ```\n *\n * @see [Search API Documentation](https://docs.tomtom.com/search-api/documentation/search-service/search-service)\n * @see [Places Quickstart Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/quickstart)\n * @see [Search Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/places/search)\n *\n * @group Places Search\n */\nexport const discoverPlaces = async (params: DiscoverPlacesParams): Promise<DiscoverPlacesResponse> => {\n if (params.route) return alongRouteSearch(params);\n if (params.geometries) return geometrySearch(params);\n return fuzzySearch(params);\n};\n\n/**\n * Search for a single place by text query.\n *\n * Convenience function that calls {@link discoverPlaces} and returns the first result.\n * Throws an error if no results are found.\n *\n * @param queryOrParams - Search query string, or the full {@link FuzzySearchParams} (bar `limit`)\n * @returns Promise resolving to the first matching place\n * @throws {Error} If no results are found for the query\n *\n * @example\n * ```typescript\n * const place = await discoverOnePlace('Vondelpark Amsterdam');\n * console.log(place.properties.poi?.name);\n *\n * const nearby = await discoverOnePlace({ query: 'coffee', geoBias: { position: [4.9041, 52.3676] }, signal });\n * ```\n *\n * @remarks\n * * Useful to quickly find a single place, particularly a POI.\n * * If you want to find a single address, consider 'geocodeOne'.\n *\n * @group Places Search\n */\nexport const discoverOnePlace = async (\n queryOrParams: string | Omit<FuzzySearchParams, 'limit'>,\n): Promise<Place<SearchPlaceProps>> => fetchFirstPlace('discoverOnePlace', queryOrParams, discoverPlaces);\n","import { z } from 'zod';\nimport { pointGeoBiasSchema } from '../shared/schema/geoBiasSchema';\nimport { geographicFiltersSchema } from '../shared/schema/searchFiltersSchema';\n\nconst autocompleteSearchRequestMandatory = z.object({\n query: z.string(),\n});\n\nconst autocompleteSearchRequestOptional = z.object({\n geoBias: pointGeoBiasSchema.optional(),\n limit: z.number().max(100).optional(),\n filters: geographicFiltersSchema.optional(),\n resultTypes: z.array(z.string()).optional(),\n});\n\n/**\n * @ignore\n */\nexport const autocompleteSearchRequestSchema = autocompleteSearchRequestMandatory.extend(\n autocompleteSearchRequestOptional.shape,\n);\n","import type { GetObject } from '../shared';\nimport { resolvePlacesEndpointUrl } from '../shared/request/placesEndpoint';\nimport {\n appendByJoiningParamValue,\n appendGeoBiasParams,\n appendOptionalParam,\n appendPlacesLanguageParam,\n buildPlacesRequestHeaders,\n} from '../shared/request/requestBuildingUtils';\nimport type { AutocompleteSearchParams } from './types';\n\nconst buildUrlBasePath = (mergedOptions: AutocompleteSearchParams): string =>\n resolvePlacesEndpointUrl(mergedOptions, `autocomplete/${encodeURIComponent(mergedOptions.query)}.json`);\n\n/**\n * Default function for building autocomplete request from {@link AutocompleteSearchParams}\n * @param params The autocomplete parameters, with global configuration already merged into them.\n */\nexport const buildAutocompleteSearchRequest = (params: AutocompleteSearchParams): GetObject => {\n const url = new URL(`${buildUrlBasePath(params)}`);\n const urlParams = url.searchParams;\n /**\n * Auto-complete service defaults the language to en-GB if not specified explicitly as service param\n * Or global config\n */\n params.language = params.language ?? 'en-GB';\n appendPlacesLanguageParam(urlParams, params);\n appendOptionalParam(urlParams, 'limit', params.limit);\n appendGeoBiasParams(urlParams, params.geoBias);\n appendByJoiningParamValue(urlParams, 'countrySet', params.filters?.countries);\n appendByJoiningParamValue(urlParams, 'resultSet', params.resultTypes);\n\n return { url, headers: buildPlacesRequestHeaders(params) };\n};\n","import { poiIDsToCategories } from '@tomtom-org/maps-sdk/core';\nimport { latLonAPIToPosition } from '../shared/geometry';\nimport type {\n AutocompleteSearchCategorySegmentAPI,\n AutocompleteSearchResponse,\n AutocompleteSearchResponseAPI,\n AutocompleteSearchResultAPI,\n} from './types';\n\nconst isCategorySegment = (\n segment: AutocompleteSearchResultAPI['segments'][number],\n): segment is AutocompleteSearchCategorySegmentAPI => segment.type === 'category';\n\nconst parseResults = (results: AutocompleteSearchResultAPI[]): AutocompleteSearchResponse['results'] =>\n results.map((result) => ({\n segments: result.segments.map((segment) => {\n if (isCategorySegment(segment)) {\n const { id, ...rest } = segment;\n return { ...rest, category: poiIDsToCategories[Number(id)] };\n }\n return segment;\n }),\n }));\n\n/**\n * Default function to parse autocomplete response.\n * @param apiResponse The API response.\n */\nexport const parseAutocompleteSearchResponse = (\n apiResponse: AutocompleteSearchResponseAPI,\n): AutocompleteSearchResponse => {\n const { position, ...geoBias } = apiResponse.context.geoBias || {};\n return {\n context: {\n ...apiResponse.context,\n geoBias: {\n ...(position && { position: latLonAPIToPosition(position) }),\n radiusMeters: geoBias.radius,\n },\n },\n results: parseResults(apiResponse.results),\n };\n};\n","import type { GetObject, ServiceTemplate } from '../shared';\nimport { get } from '../shared/fetch';\nimport { autocompleteSearchRequestSchema } from './autocompleteSearchRequestSchema';\nimport { buildAutocompleteSearchRequest } from './requestBuilder';\nimport { parseAutocompleteSearchResponse } from './responseParser';\nimport type { AutocompleteSearchParams, AutocompleteSearchResponse, AutocompleteSearchResponseAPI } from './types';\n\n/**\n * Autocomplete service template type.\n * @ignore\n */\nexport type AutocompleteSearchTemplate = ServiceTemplate<\n AutocompleteSearchParams,\n GetObject,\n AutocompleteSearchResponseAPI,\n AutocompleteSearchResponse\n>;\n\n/**\n * Autocomplete service template main implementation.\n * @ignore\n */\nexport const autocompleteSearchTemplate: AutocompleteSearchTemplate = {\n requestValidation: { schema: autocompleteSearchRequestSchema },\n buildRequest: buildAutocompleteSearchRequest,\n sendRequest: get,\n parseResponse: parseAutocompleteSearchResponse,\n};\n","import { callService } from '../shared/serviceTemplate';\nimport { autocompleteSearchTemplate } from './autocompleteSearchTemplate';\nimport type { AutocompleteSearchParams, AutocompleteSearchResponse } from './types';\n\n/**\n * Recognises the brands and categories a partial query names, as typed segments.\n *\n * @see [Autocomplete API Documentation](https://docs.tomtom.com/search-api/documentation/autocomplete-service/autocomplete)\n *\n * @ignore (exposed via 'getSearchSuggestions')\n */\nexport const autocompleteSearch = async (params: AutocompleteSearchParams): Promise<AutocompleteSearchResponse> =>\n callService(params, autocompleteSearchTemplate, 'Autocomplete');\n","import type { Place, SearchPlaceProps } from '@tomtom-org/maps-sdk/core';\nimport { partition, uniqBy } from 'lodash-es';\nimport type { AutocompleteSearchResult, AutocompleteSearchSegment } from '../autocomplete-search/types';\nimport type { PlaceSuggestion, RefinementSuggestion, SearchSuggestion } from './types/searchSuggestionTypes';\n\n/**\n * What the query names among brands and categories: nothing, a concept the places do not bear out,\n * or one to lead the list with.\n *\n * @ignore\n */\nexport type ConceptMatch =\n | { status: 'none' }\n | { status: 'withheld' }\n | { status: 'shown'; suggestion: RefinementSuggestion };\n\n// Accent- and case-insensitive words: \"Café De Gieter\" → [\"cafe\", \"de\", \"gieter\"], \"H&M\" → [\"h\", \"m\"].\nconst words = (text: string): string[] =>\n text\n .normalize('NFD')\n .replace(/\\p{Diacritic}/gu, '')\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean);\n\n// Whether the query reads as the start of `name` from one of its word boundaries, ignoring spacing and\n// punctuation: \"mcdon\" → \"McDonald's\", \"charging\" → \"Electric Vehicle Charging Station\", but not\n// \"amster\" → \"Mastermate\".\nconst startsAtWordOf = (name: string, query: string): boolean => {\n const target = words(query).join('');\n const nameWords = words(name);\n return target !== '' && nameWords.some((_, index) => nameWords.slice(index).join('').startsWith(target));\n};\n\n// A one-word query that is most of a word in `name` — \"burger\" in \"Burgers\" — rather than the first\n// letters of a long compound, as in \"Burgerluchthaven\".\nconst coversMostOfWordIn = (name: string, query: string): boolean => {\n const [queryWord, ...rest] = words(query);\n return (\n rest.length === 0 &&\n words(name).some((nameWord) => nameWord.startsWith(queryWord) && queryWord.length * 2 >= nameWord.length)\n );\n};\n\nconst namesOf = (segment: Exclude<AutocompleteSearchSegment, { type: 'plaintext' }>): string[] =>\n segment.type === 'category' && segment.matchedAlternativeName\n ? [segment.value, segment.matchedAlternativeName]\n : [segment.value];\n\n// Every typed word begins some word of the title: \"cafe\" matches \"Café De Gieter\", not \"Coffeeshop Tyson\".\nconst titleMatches = (title: string, query: string): boolean => {\n const titleWords = words(title);\n return words(query).every((queryWord) => titleWords.some((titleWord) => titleWord.startsWith(queryWord)));\n};\n\n/**\n * The brand or category segment the whole query names, if any.\n *\n * @remarks\n * An autocomplete result decomposes the whole query, so a single recognised segment covers everything\n * typed: \"hot\" → Hotel, but not \"baker st\", which leaves \"st\" as plaintext.\n *\n * The segment must also read as the query from a word start of its name or synonym, which rules out\n * \"Mastermate\" for \"amster\". The first segment the query covers most of a word of wins, else the first.\n *\n * @ignore\n */\nexport const findConceptSegment = (\n query: string,\n results: AutocompleteSearchResult[],\n): Exclude<AutocompleteSearchSegment, { type: 'plaintext' }> | undefined => {\n const candidates = results.flatMap(({ segments }) => {\n const [segment] = segments;\n return segments.length === 1 &&\n segment.type !== 'plaintext' &&\n namesOf(segment).some((name) => startsAtWordOf(name, query))\n ? [segment]\n : [];\n });\n return (\n candidates.find((segment) => namesOf(segment).some((name) => coversMostOfWordIn(name, query))) ?? candidates[0]\n );\n};\n\nconst BRAND_EVIDENCE_DEPTH = 5;\n\n/**\n * Whether the leading search results bear out a recognised brand.\n *\n * @remarks\n * A brand name that merely starts like the query — \"London Fire Brigade\" for \"londo\" — has no outlets\n * among the places the same query finds. A brand the user means does.\n *\n * @ignore\n */\nexport const searchCarriesBrand = (brand: string, places: Place<SearchPlaceProps>[]): boolean => {\n const target = words(brand).join('');\n return places\n .slice(0, BRAND_EVIDENCE_DEPTH)\n .some((place) => place.properties.poi?.brands?.some((name) => words(name).join('') === target));\n};\n\n/**\n * The default blend: at most one brand or category first, then places.\n *\n * @remarks\n * Modelled on Places Search v3's `suggest`, which never offers more than one concept and leads with it.\n *\n * Once the query names a brand or category, shown or withheld, that concept stands for \"places of this\n * kind\", so places named after the typed words move up: \"Café De Gieter\" ahead of a coffeeshop for \"cafe\".\n *\n * @ignore\n */\nexport const blendSuggestions = ({\n query,\n concept,\n places,\n maxResults,\n}: {\n query: string;\n concept: ConceptMatch;\n places: PlaceSuggestion[];\n maxResults: number;\n}): SearchSuggestion[] => {\n const distinctPlaces = uniqBy(places, (place) => place.id);\n const ordered =\n concept.status === 'none'\n ? distinctPlaces\n : partition(distinctPlaces, (place) => titleMatches(place.title, query)).flat();\n return [...(concept.status === 'shown' ? [concept.suggestion] : []), ...ordered].slice(0, maxResults);\n};\n","import {\n getPosition,\n type HasLngLat,\n type Place,\n type PlaceType,\n type SearchPlaceProps,\n} from '@tomtom-org/maps-sdk/core';\nimport type { Position } from 'geojson';\nimport { countBy, maxBy, minBy, omit, uniqBy } from 'lodash-es';\nimport { autocompleteSearch } from '../autocomplete-search/autocompleteSearch';\nimport type { AutocompleteSearchResult, AutocompleteSearchSegment } from '../autocomplete-search/types';\nimport type { FuzzySearchParams } from '../fuzzy-search/types';\nimport { discoverPlaces } from '../search/discoverPlaces';\nimport { SDKAbortError, SDKError, SDKServiceError, type SearchIndexType } from '../shared';\nimport { blendSuggestions, type ConceptMatch, findConceptSegment, searchCarriesBrand } from './blend';\nimport type {\n PlaceSuggestion,\n RefinementSuggestion,\n SearchSuggestionKind,\n SearchSuggestionsParams,\n SearchSuggestionsResponse,\n} from './types/searchSuggestionTypes';\n\nconst SERVICE_NAME = 'SearchSuggestions';\n\nconst DEFAULT_MAX_RESULTS = 5;\n// v3's `suggest` caps at 10; match it so the ceiling does not move when the backend does.\nconst MAX_RESULTS_CEILING = 10;\n\n// The two arms answer for disjoint halves of `SearchSuggestionKind`, so which kinds were asked for is\n// also which requests need to go out. Keyed on the refinement kinds so that a new one fails to\n// compile until it is listed; everything else is something the search arm produces.\nconst REFINEMENT_KINDS: Record<RefinementSuggestion['kind'], true> = { brand: true, category: true };\n\nconst isRefinementKind = (kind: SearchSuggestionKind): kind is RefinementSuggestion['kind'] => kind in REFINEMENT_KINDS;\n\nconst splitKinds = (kinds: SearchSuggestionKind[] | undefined) => {\n const placeKinds = kinds?.filter((kind) => !isRefinementKind(kind));\n const refinementKinds = kinds?.filter(isRefinementKind);\n return {\n placeKinds,\n refinementKinds,\n wantsPlaces: !kinds || Boolean(placeKinds?.length),\n wantsRefinements: !kinds || Boolean(refinementKinds?.length),\n };\n};\n\n// The two directions between suggestion kinds and V2's search indexes and place types. V2 has three\n// flavours of address that v3 folds into one; following v3 keeps the kinds stable across backends.\nconst INDEXES_BY_PLACE_KIND: Record<PlaceSuggestion['kind'], SearchIndexType[]> = {\n place: ['POI'],\n address: ['PAD', 'Addr', 'XStr'],\n street: ['Str'],\n area: ['Geo'],\n};\n\nconst KIND_BY_PLACE_TYPE: Record<PlaceType, PlaceSuggestion['kind']> = {\n POI: 'place',\n Street: 'street',\n Geography: 'area',\n 'Point Address': 'address',\n 'Address Range': 'address',\n 'Cross Street': 'address',\n};\n\nconst EARTH_RADIUS_METERS = 6371008.8;\n\nconst toRadians = (degrees: number) => (degrees * Math.PI) / 180;\n\n// Haversine: V2 measures `dist` only from a point it was sent, which a bounding-box search has none of.\nconst metersBetween = ([lng1, lat1]: Position, [lng2, lat2]: Position): number => {\n const halfChord =\n Math.sin(toRadians(lat2 - lat1) / 2) ** 2 +\n Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(toRadians(lng2 - lng1) / 2) ** 2;\n return 2 * EARTH_RADIUS_METERS * Math.asin(Math.sqrt(halfChord));\n};\n\nconst distanceFrom = (place: Place<SearchPlaceProps>, origin: Position | undefined): number | undefined =>\n place.properties.distance ?? (origin && metersBetween(origin, place.geometry.coordinates));\n\n// v3 leaves the caller's own country off the subtitles. V2 does not say where the caller is, so the\n// country of the place nearest the origin stands in for it, or without one the country most places are in.\nconst homeCountryCodeOf = (places: Place<SearchPlaceProps>[], origin: Position | undefined): string | undefined => {\n if (origin) return minBy(places, (place) => distanceFrom(place, origin))?.properties.address.countryCode;\n\n const counts = countBy(places, (place) => place.properties.address.countryCode);\n return maxBy(Object.keys(counts), (countryCode) => counts[countryCode]);\n};\n\n// The display fields v3 returns: a POI's name over its address lines, or an address's first line over\n// the rest. The search already returned the whole place, so resolving one is immediate; what only this\n// query decided stays on the suggestion, so the place matches what `getPlaceDetails` returns for its id.\nconst toPlaceSuggestion = (\n place: Place<SearchPlaceProps>,\n homeCountryCode: string | undefined,\n origin: Position | undefined,\n): PlaceSuggestion => {\n const { type, address, poi } = place.properties;\n const addressLines = address.freeformAddress.split(', ').filter(Boolean);\n const [title = '', ...subtitles] = poi?.name ? [poi.name, ...addressLines] : addressLines;\n const foreignCountry = address.countryCode !== homeCountryCode ? address.country : undefined;\n return {\n kind: KIND_BY_PLACE_TYPE[type],\n id: String(place.id),\n title,\n subtitles: foreignCountry ? [...subtitles, foreignCountry] : subtitles,\n distanceInMeters: distanceFrom(place, origin),\n resolve: async () => ({ ...place, properties: omit(place.properties, ['distance', 'score']) }),\n };\n};\n\nconst toPlaceSuggestions = (features: Place<SearchPlaceProps>[], origin: HasLngLat | undefined): PlaceSuggestion[] => {\n const originPosition = getPosition(origin) ?? undefined;\n const homeCountryCode = homeCountryCodeOf(features, originPosition);\n return features.map((place) => toPlaceSuggestion(place, homeCountryCode, originPosition));\n};\n\n// Either arm's failure is this call's, so it is reported under this call's name.\nconst throwAsSearchSuggestionsError = (error: unknown): never => {\n if (error instanceof SDKAbortError) throw new SDKAbortError(SERVICE_NAME, error.cause);\n if (error instanceof SDKServiceError) throw new SDKServiceError(error.message, SERVICE_NAME, error.status);\n if (error instanceof SDKError) throw new SDKError(error.message, SERVICE_NAME, error.issues);\n throw error;\n};\n\nconst toRefinement = (\n segment: Exclude<AutocompleteSearchSegment, { type: 'plaintext' }>,\n scope: FuzzySearchParams,\n): RefinementSuggestion => {\n const narrowing = segment.type === 'brand' ? { poiBrands: [segment.value] } : { poiCategories: [segment.category] };\n const refineParams: FuzzySearchParams = { ...scope, filters: { ...scope.filters, ...narrowing } };\n const refine = (signal?: AbortSignal) => discoverPlaces({ ...refineParams, ...(signal && { signal }) });\n const shared = { title: segment.value, refineParams, refine };\n return segment.type === 'brand'\n ? { ...shared, kind: 'brand' }\n : { ...shared, kind: 'category', category: segment.category };\n};\n\n// Every recognised segment across the response, for a caller who asked for refinements only.\nconst toRefinementSuggestions = (\n results: AutocompleteSearchResult[],\n scope: FuzzySearchParams,\n): RefinementSuggestion[] =>\n uniqBy(\n results.flatMap((result) =>\n result.segments.flatMap((segment) => (segment.type === 'plaintext' ? [] : [toRefinement(segment, scope)])),\n ),\n (refinement) => `${refinement.kind}:${refinement.title}`,\n );\n\n// The concept to lead with, if any: the brand or category the query names, a brand only when the\n// places found for the same query bear it out.\nconst pickConcept = (\n query: string,\n results: AutocompleteSearchResult[],\n features: Place<SearchPlaceProps>[],\n scope: FuzzySearchParams,\n): ConceptMatch => {\n const segment = findConceptSegment(query, results);\n if (!segment) return { status: 'none' };\n\n if (segment.type === 'brand' && !searchCarriesBrand(segment.value, features)) return { status: 'withheld' };\n\n return { status: 'shown', suggestion: toRefinement(segment, scope) };\n};\n\n/**\n * One query surface over places, brands and categories.\n *\n * @remarks\n * Fans out to autocomplete and search in parallel and blends them into one ranked list, so \"pizz\"\n * gets the *Pizzeria* category and the pizzerias down the road together. A complete query works as\n * well as a partial one.\n *\n * - **Blend**: the SDK's, modelled on Places Search v3. At most one brand or category leads, and only\n * when the whole query names it; places follow.\n * - **Cost**: two requests per call; narrowing `filters.kinds` to one side of {@link SearchSuggestionKind}\n * skips the other. No debouncing or caching, which belong in the caller's input loop.\n * - **Geography**: `filters.boundingBox` restricts and `origin` measures distance. V2 takes one\n * geographic input per request, so inside a box `origin` does not rank the places.\n * - **Cancellation**: both requests share `signal`, so aborting drops the pair.\n *\n * @example\n * ```typescript\n * const { suggestions } = await getSearchSuggestions({ query: 'pizz' });\n *\n * for (const suggestion of suggestions) {\n * if (suggestion.kind === 'category' || suggestion.kind === 'brand') {\n * // A refinement: run the follow-up it describes.\n * const narrowed = await suggestion.refine();\n * } else {\n * const place = await suggestion.resolve();\n * map.flyTo({ center: place.geometry.coordinates });\n * }\n * }\n * ```\n *\n * @group Places Search\n */\nexport const getSearchSuggestions = async (parameters: SearchSuggestionsParams): Promise<SearchSuggestionsResponse> => {\n const { query, maxResults, filters, origin, view, signal, ...config } = parameters;\n const countries = filters?.countries;\n const max = Math.max(1, Math.min(maxResults ?? DEFAULT_MAX_RESULTS, MAX_RESULTS_CEILING));\n const { placeKinds, refinementKinds, wantsPlaces, wantsRefinements } = splitKinds(filters?.kinds);\n\n // V2 drops a box sent with a point, so a box restricts the places, in the list and in a follow-up\n // alike, and `origin` only measures.\n const originBias = origin && { position: origin };\n const placesBias = (filters?.boundingBox && { boundingBox: filters.boundingBox }) ?? originBias;\n\n // A follow-up inherits the caller's config and where to look, not the keystroke's signal, hooks,\n // limit or typeahead. `config` holds only keys the caller set: an own `undefined` blanks the global.\n const scope: FuzzySearchParams = {\n ...omit(config, ['onAPIRequest', 'onAPIResponse']),\n query: '',\n ...(countries && { filters: { countries } }),\n geoBias: placesBias,\n view,\n };\n\n // The search arm always asks for the ceiling, so the blend has places to promote when a category\n // takes the first slot.\n const searchParams: FuzzySearchParams = {\n ...config,\n signal,\n query,\n limit: MAX_RESULTS_CEILING,\n view,\n typeahead: true,\n geoBias: placesBias,\n filters: {\n countries,\n indexes: placeKinds?.flatMap((kind) => INDEXES_BY_PLACE_KIND[kind]),\n poiCategories: filters?.poiTypes,\n geographyTypes: filters?.areaTypes,\n },\n };\n\n const [searchResponse, autocompleteResponse] = await Promise.all([\n wantsPlaces ? discoverPlaces(searchParams) : undefined,\n wantsRefinements\n ? autocompleteSearch({\n ...config,\n signal,\n query,\n limit: max,\n ...(countries && { filters: { countries } }),\n geoBias: originBias,\n resultTypes: refinementKinds ?? ['brand', 'category'],\n })\n : undefined,\n ]).catch(throwAsSearchSuggestionsError);\n\n const autocompleteResults = autocompleteResponse?.results ?? [];\n if (!wantsPlaces) return { suggestions: toRefinementSuggestions(autocompleteResults, scope).slice(0, max) };\n\n const features = (searchResponse?.features ?? []).filter(\n (place) => !placeKinds || placeKinds.includes(KIND_BY_PLACE_TYPE[place.properties.type]),\n );\n return {\n suggestions: blendSuggestions({\n query,\n concept: pickConcept(query, autocompleteResults, features, scope),\n places: toPlaceSuggestions(features, origin),\n maxResults: max,\n }),\n };\n};\n","import type { AreaAnalyticsDateInput } from './types/trafficAreaAnalyticsParams';\n\n/**\n * The latest date the API accepts, in days before today.\n */\nexport const LATEST_DATE_DAYS_AGO = 2;\n\n/**\n * The date an omitted `endDate` defaults to, in days before today: the API publishes a day 24 to 48 hours after it\n * ends, so the latest date it accepts has no data yet during part of each day.\n */\nexport const DEFAULT_END_DATE_DAYS_AGO = 3;\n\nexport const ONE_DAY_MS = 24 * 60 * 60 * 1000;\n\n// In UTC: the API works in `YYYY-MM-DD` dates, which are what `toISOString()` yields.\nexport const addDays = (date: Date, days: number): Date => {\n const result = new Date(date);\n result.setUTCDate(result.getUTCDate() + days);\n return result;\n};\n\nexport const daysAgo = (days: number): Date => addDays(new Date(), -days);\n\nexport const toDateString = (date: AreaAnalyticsDateInput): string =>\n date instanceof Date ? date.toISOString().slice(0, 10) : date;\n\n/**\n * Midnight UTC of the calendar day the request sends for `date`.\n */\nexport const startOfDayUTC = (date: AreaAnalyticsDateInput): number => Date.parse(toDateString(date));\n\nconst OFFSET = /(?:Z|[+-]\\d{2}:\\d{2})$/;\n\n/**\n * Reads a date-time from the response, such as `2024-08-06T07:00`, as UTC when it has no offset: the API's clock is\n * UTC, but `new Date` would read an offset-less date-time as local time.\n */\nexport const parseDateTime = (dateTime: string): Date => new Date(OFFSET.test(dateTime) ? dateTime : `${dateTime}Z`);\n","import type { AreaAnalyticsMetricKey } from '@tomtom-org/maps-sdk/core';\nimport type { MultiPolygon, Polygon } from 'geojson';\nimport type { CommonServiceParams } from '../../shared';\n\n// ---------------------------------------------------------------------------\n// Functional Road Classes\n// ---------------------------------------------------------------------------\n\n/**\n * Ordered list of Functional Road Class identifiers, from highest importance (motorways) to lowest.\n *\n * @remarks\n * The index of each class in this array corresponds to its FRC value in the API (0 for 'MOTORWAY', 1 for 'MAJOR_ROAD', ..., 8 for 'OTHER_ROAD').\n *\n * @see https://docs.tomtom.com/traffic-stats/documentation/product-information/faq#what-are-functional-road-classes-frc\n *\n * @group Traffic\n */\nexport const functionalRoadClasses = [\n 'MOTORWAY',\n 'MAJOR_ROAD',\n 'OTHER_MAJOR_ROAD',\n 'SECONDARY_ROAD',\n 'LOCAL_CONNECTING_ROAD',\n 'LOCAL_ROAD_HIGH_IMPORTANCE',\n 'LOCAL_ROAD',\n 'LOCAL_ROAD_MINOR_IMPORTANCE',\n 'OTHER_ROAD',\n] as const;\n\n/**\n * A Functional Road Class (FRC) identifier — classifies road segments by their importance\n * in the road network.\n *\n * | Value | Description |\n * |---|---|\n * | `'MOTORWAY'` | Motorways, freeways, and major roads |\n * | `'MAJOR_ROAD'` | Major roads, less important than motorways |\n * | `'OTHER_MAJOR_ROAD'` | Other major roads connecting neighbouring regions |\n * | `'SECONDARY_ROAD'` | Secondary roads linking parts of the same region |\n * | `'LOCAL_CONNECTING_ROAD'` | Local roads providing settlement access |\n * | `'LOCAL_ROAD_HIGH_IMPORTANCE'` | Local roads of high importance within a settlement |\n * | `'LOCAL_ROAD'` | Local roads within settlement sections |\n * | `'LOCAL_ROAD_MINOR_IMPORTANCE'` | Local roads of minor importance (dead-ends, alleys) |\n * | `'OTHER_ROAD'` | Other roads (paths, cycle routes, pedestrian infrastructure) |\n *\n * @group Traffic\n */\nexport type FunctionalRoadClass = (typeof functionalRoadClasses)[number];\n\n// ---------------------------------------------------------------------------\n// Date input\n// ---------------------------------------------------------------------------\n\n/**\n * A date value accepted by Traffic Area Analytics — either a `Date` object or\n * an ISO `'YYYY-MM-DD'` string.\n * `Date` objects are automatically converted to `YYYY-MM-DD` format before\n * being sent to the API.\n *\n * @group Traffic\n */\nexport type AreaAnalyticsDateInput = string | Date;\n\n// ---------------------------------------------------------------------------\n// Date selection — exactly one variant must be supplied\n// ---------------------------------------------------------------------------\n\n/**\n * Continuous date-range variant: analyse traffic between `startDate` and `endDate`.\n *\n * - `endDate` is optional; when omitted it defaults to **three days ago**, the latest date the\n * service always has data for.\n * - The range spans at least one and at most **31** UTC calendar days.\n * - Mutually exclusive with {@link TrafficAreaAnalyticsDays}.\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsDateRange = {\n /**\n * Start date of the analysis period.\n * Accepts a `Date` object or an ISO `'YYYY-MM-DD'` string.\n *\n * @example '2024-08-01'\n * @example new Date('2024-08-01')\n */\n startDate: AreaAnalyticsDateInput;\n\n /**\n * End date of the analysis period (inclusive).\n * Accepts a `Date` object or an ISO `'YYYY-MM-DD'` string.\n * When omitted, defaults to **three days ago**.\n *\n * @remarks Must be within 31 days from `startDate`, and no later than two days before today.\n *\n * @example '2024-08-07'\n * @example new Date('2024-08-07')\n */\n endDate?: AreaAnalyticsDateInput;\n\n days?: never;\n};\n\n/**\n * Specific-dates variant: analyse traffic on an explicit list of dates.\n *\n * Useful for non-consecutive dates such as \"all Mondays in August\".\n * Mutually exclusive with {@link TrafficAreaAnalyticsDateRange}.\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsDays = {\n /**\n * List of specific dates to include in the analysis.\n * Accepts `Date` objects or ISO `'YYYY-MM-DD'` strings.\n *\n * @example ['2024-08-05', '2024-08-12', '2024-08-19', '2024-08-26']\n */\n days: AreaAnalyticsDateInput[];\n\n startDate?: never;\n endDate?: never;\n};\n\n// ---------------------------------------------------------------------------\n// Main params type\n// ---------------------------------------------------------------------------\n\n/**\n * Parameters for the Traffic Area Analytics Lite service.\n *\n * @remarks\n * Supply **either** `startDate` (+ optional `endDate`) **or** `days` — these\n * two options are mutually exclusive.\n *\n * The lite endpoint runs analysis synchronously and returns results immediately,\n * with the constraint that the date range must not exceed 31 days and only a\n * single region may be submitted.\n *\n * @example\n * ```typescript\n * // Continuous range: endDate defaults to three days ago when omitted\n * const result = await trafficAreaAnalytics({\n * startDate: '2024-08-01',\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],\n * hours: [7, 8, 9, 17, 18],\n * metrics: ['speed', 'congestionLevel'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.896128, 52.382402], [4.875701, 52.368459], [4.923611, 52.36341], [4.896128, 52.382402]]]\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Specific dates\n * const result = await trafficAreaAnalytics({\n * days: ['2024-08-05', '2024-08-12', '2024-08-19', '2024-08-26'],\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD'],\n * hours: [8, 9, 17, 18],\n * metrics: ['speed'],\n * geometry: polygon\n * });\n * ```\n *\n * @group Traffic\n */\nexport type TrafficAreaAnalyticsParams = CommonServiceParams &\n TrafficAreaAnalyticsBaseParams &\n (TrafficAreaAnalyticsDateRange | TrafficAreaAnalyticsDays);\n\n/**\n * @ignore\n */\ntype TrafficAreaAnalyticsBaseParams = {\n /**\n * Optional name for the analysis report.\n */\n name?: string;\n\n /**\n * Traffic metrics to include in the analysis, or `'all'` to include every metric.\n *\n * @remarks\n * At least one metric must be provided, or pass `'all'` as a shorthand for all five metrics.\n *\n * @example ['speed', 'congestionLevel', 'freeFlowSpeed']\n * @example 'all'\n */\n metrics: AreaAnalyticsMetricKey[] | 'all';\n\n /**\n * Functional road classes to include in the analysis, or `'all'` to include every class.\n *\n * @remarks\n * At least one value must be provided, or pass `'all'` as a shorthand for all nine classes.\n *\n * @example ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD']\n * @example 'all'\n */\n functionalRoadClasses: FunctionalRoadClass[] | 'all';\n\n /**\n * Hours of the day in UTC (0–23) to include in the analysis, or `'all'` to include every hour.\n *\n * @remarks\n * Provide at least one hour, or pass `'all'` as a shorthand for `[0, 1, 2, ..., 23]`.\n *\n * @example [7, 8, 9, 17, 18]\n * @example 'all'\n */\n hours: number[] | 'all';\n\n /**\n * The GeoJSON Polygon or MultiPolygon geometry defining the analysis region.\n *\n * @remarks\n * The lite endpoint accepts exactly one region. For multi-region analysis,\n * use the standard (async) report creation endpoint.\n */\n geometry: Polygon | MultiPolygon;\n};\n","import { type AreaAnalyticsMetricKey, areaAnalyticsMetricKeys } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput } from '../shared';\nimport { appendCommonParams } from '../shared/request/requestBuildingUtils';\nimport { DEFAULT_END_DATE_DAYS_AGO, daysAgo, toDateString } from './dates';\nimport type { AreaAnalyticsDataTypeAPI, AreaAnalyticsRequestBody, MetricsAPI } from './types/apiTypes';\nimport type { FunctionalRoadClass, TrafficAreaAnalyticsParams } from './types/trafficAreaAnalyticsParams';\nimport { functionalRoadClasses } from './types/trafficAreaAnalyticsParams';\n\nconst AREA_ANALYTICS_URL_PATH = '/areaanalytics/reports/lite';\n\n/**\n * Each metric's names in the API: its data type in the request and the response `dataTypes`, and its abbreviated\n * field in the response metric values.\n */\nexport const API_NAMES_BY_METRIC_KEY: Record<\n AreaAnalyticsMetricKey,\n { dataType: AreaAnalyticsDataTypeAPI; field: keyof MetricsAPI }\n> = {\n speed: { dataType: 'SPEED', field: 'v' },\n freeFlowSpeed: { dataType: 'FREE_FLOW_SPEED', field: 'fv' },\n congestionLevel: { dataType: 'CONGESTION_LEVEL', field: 'c' },\n travelTime: { dataType: 'TRAVEL_TIME', field: 't' },\n networkLength: { dataType: 'NETWORK_LENGTH', field: 'l' },\n};\n\nconst buildUrlBasePath = (params: TrafficAreaAnalyticsParams): string =>\n `${params.commonBaseURL}${AREA_ANALYTICS_URL_PATH}`;\n\nexport const ALL_HOURS = Array.from({ length: 24 }, (_, i) => i);\n\nconst frcToNumber = (frc: FunctionalRoadClass): number => functionalRoadClasses.indexOf(frc);\n\n/**\n * Default method for building a Traffic Area Analytics request from {@link TrafficAreaAnalyticsParams}.\n * @param params The traffic area analytics parameters, with global configuration already merged.\n */\nexport const buildTrafficAreaAnalyticsRequest = (\n params: TrafficAreaAnalyticsParams,\n): FetchInput<AreaAnalyticsRequestBody> => {\n const url = new URL(buildUrlBasePath(params));\n appendCommonParams(url.searchParams, params);\n\n const datePart = Array.isArray(params.days)\n ? { days: params.days.map(toDateString) }\n : {\n startDate: toDateString(params.startDate ?? daysAgo(DEFAULT_END_DATE_DAYS_AGO + 1)),\n endDate: toDateString(params.endDate ?? daysAgo(DEFAULT_END_DATE_DAYS_AGO)),\n };\n\n const body: AreaAnalyticsRequestBody = {\n name: params.name ?? `Maps SDK JS Area Analytics Report ${new Date().toISOString()}`,\n ...datePart,\n dataTypes: (params.metrics === 'all' ? areaAnalyticsMetricKeys : params.metrics).map(\n (metricKey) => API_NAMES_BY_METRIC_KEY[metricKey].dataType,\n ),\n frcs:\n params.functionalRoadClasses === 'all'\n ? functionalRoadClasses.map((_, i) => i)\n : params.functionalRoadClasses.map(frcToNumber),\n hours: params.hours === 'all' ? ALL_HOURS : params.hours,\n features: [{ type: 'Feature', geometry: params.geometry }],\n };\n\n return { method: 'POST', url, data: body };\n};\n","import {\n AreaAnalyticsAnomaly,\n AreaAnalyticsAverageEntry,\n AreaAnalyticsCollectionProperties,\n AreaAnalyticsFeature,\n AreaAnalyticsFeatureProperties,\n AreaAnalyticsMetricKey,\n AreaAnalyticsMetricRange,\n AreaAnalyticsMetrics,\n AreaAnalyticsTileEntry,\n AreaAnalyticsTimedData,\n areaAnalyticsMetricKeys,\n BBox,\n bboxFromGeoJSON,\n TrafficAreaAnalytics,\n} from '@tomtom-org/maps-sdk/core';\nimport { addDays, ONE_DAY_MS, parseDateTime, startOfDayUTC } from './dates';\nimport { API_NAMES_BY_METRIC_KEY } from './requestBuilder';\nimport type {\n AnomaliesAPI,\n AnomalyAPI,\n AreaAnalyticsCollectionPropertiesAPI,\n AreaAnalyticsFeatureAPI,\n AreaAnalyticsResponseAPI,\n AverageEntryAPI,\n DayOfWeekAPI,\n FeaturePropertiesAPI,\n MetricFieldsAPI,\n MetricsAPI,\n TiledEntryAPI,\n TimedEntryAPI,\n} from './types/apiTypes';\n\ntype ByMetricKey<Value> = Partial<Record<AreaAnalyticsMetricKey, Value>>;\n\n/**\n * Renames the abbreviated metric fields the API sent, parsing each value, and leaves out the ones it did not send.\n */\nconst parseMetricFields = <APIValue, Value>(\n api: MetricFieldsAPI<APIValue>,\n parseValue: (apiValue: APIValue) => Value,\n): ByMetricKey<Value> => {\n const result: ByMetricKey<Value> = {};\n // A callback, not `for…of`: Terser can give `apiValue` the name it gives the imported `areaAnalyticsMetricKeys`,\n // and an ES5 transpile of the bundle, such as Sandpack's, then hoists `apiValue` over the import.\n areaAnalyticsMetricKeys.forEach((metricKey) => {\n const apiValue = api[API_NAMES_BY_METRIC_KEY[metricKey].field];\n if (apiValue !== undefined) result[metricKey] = parseValue(apiValue);\n });\n return result;\n};\n\nconst parseMetrics = (api: MetricsAPI): AreaAnalyticsMetrics => parseMetricFields(api, (value) => value);\n\n// The response may name data types this SDK version doesn't know.\nconst parseMetricKey = (dataType: string): AreaAnalyticsMetricKey | undefined =>\n areaAnalyticsMetricKeys.find((metricKey) => API_NAMES_BY_METRIC_KEY[metricKey].dataType === dataType);\n\ntype PeriodStart = { year: number; month: number; date: Date; hour: number };\n\nconst parsePeriodStart = (time: TimedEntryAPI['time']): PeriodStart => {\n const start = parseDateTime(time);\n return {\n year: start.getUTCFullYear(),\n month: start.getUTCMonth() + 1,\n date: new Date(startOfDayUTC(start)),\n hour: start.getUTCHours(),\n };\n};\n\ntype CalendarGranularity = Exclude<keyof AreaAnalyticsTimedData, 'average'>;\n\ntype TimeIdentifiers<Granularity extends CalendarGranularity> = Omit<\n NonNullable<AreaAnalyticsTimedData[Granularity]>[number],\n AreaAnalyticsMetricKey\n>;\n\n// An ISO 8601 week belongs to the year of its Thursday, and week 1 is the one holding that year's first Thursday.\nconst parseISOWeek = (date: Date): TimeIdentifiers<'weekly'> => {\n const thursday = addDays(date, 4 - (date.getUTCDay() || 7));\n const year = thursday.getUTCFullYear();\n const dayOfYear = (thursday.getTime() - Date.UTC(year, 0, 1)) / ONE_DAY_MS;\n return { year, week: Math.floor(dayOfYear / 7) + 1 };\n};\n\nconst IDENTIFIERS_BY_GRANULARITY: {\n [Granularity in CalendarGranularity]: (start: PeriodStart) => TimeIdentifiers<Granularity>;\n} = {\n yearly: ({ year }) => ({ year }),\n monthly: ({ year, month }) => ({ year, month }),\n weekly: ({ date }) => parseISOWeek(date),\n daily: ({ date }) => ({ date }),\n hourly: ({ date, hour }) => ({ date, hour }),\n};\n\nconst parseCalendarEntries = <Granularity extends CalendarGranularity>(\n entries: TimedEntryAPI[],\n granularity: Granularity,\n): (TimeIdentifiers<Granularity> & AreaAnalyticsMetrics)[] =>\n entries.map((entry) => ({\n ...IDENTIFIERS_BY_GRANULARITY[granularity](parsePeriodStart(entry.time)),\n ...parseMetrics(entry),\n }));\n\ntype DayOfWeek = AreaAnalyticsAverageEntry['day'];\n\n// Keyed by `string`: the response may name a day this SDK version doesn't know, and its entry is dropped.\nconst DAY_BY_API_DAY_OF_WEEK: Partial<Record<string, DayOfWeek>> = {\n MONDAY: 1,\n TUESDAY: 2,\n WEDNESDAY: 3,\n THURSDAY: 4,\n FRIDAY: 5,\n SATURDAY: 6,\n SUNDAY: 7,\n} satisfies Record<DayOfWeekAPI, DayOfWeek>;\n\nconst parseAverageEntry = (entry: AverageEntryAPI): AreaAnalyticsAverageEntry | undefined => {\n const [apiDay, hour] = entry.time.split('-');\n // `hasOwn` keeps out the `Object.prototype` keys, such as `toString`.\n const day = Object.hasOwn(DAY_BY_API_DAY_OF_WEEK, apiDay) ? DAY_BY_API_DAY_OF_WEEK[apiDay] : undefined;\n return day === undefined ? undefined : { day, hour: Number(hour), ...parseMetrics(entry) };\n};\n\nconst hourOfWeek = ({ day, hour }: AreaAnalyticsAverageEntry): number => (day - 1) * 24 + hour;\n\nconst parseAverageEntries = (entries: AverageEntryAPI[]): AreaAnalyticsAverageEntry[] =>\n entries\n .map(parseAverageEntry)\n .filter((entry) => entry !== undefined)\n .sort((first, second) => hourOfWeek(first) - hourOfWeek(second));\n\nconst parseTileEntry = (tile: TiledEntryAPI): AreaAnalyticsTileEntry => ({\n tileCentre: [tile.lon, tile.lat],\n ...parseMetrics(tile),\n});\n\nconst parseAnomaly = (api: AnomalyAPI): AreaAnalyticsAnomaly => ({\n startDate: parseDateTime(api.startDate),\n endDate: parseDateTime(api.endDate),\n labels: api.labels.map(({ name }) => name),\n});\n\nconst parseTimedData = (api: FeaturePropertiesAPI['timedData']): AreaAnalyticsTimedData => ({\n ...(api.yearly && { yearly: parseCalendarEntries(api.yearly, 'yearly') }),\n ...(api.monthly && { monthly: parseCalendarEntries(api.monthly, 'monthly') }),\n ...(api.weekly && { weekly: parseCalendarEntries(api.weekly, 'weekly') }),\n ...(api.daily && { daily: parseCalendarEntries(api.daily, 'daily') }),\n ...(api.hourly && { hourly: parseCalendarEntries(api.hourly, 'hourly') }),\n ...(api.average && { average: parseAverageEntries(api.average) }),\n});\n\nconst parseAnomalies = (api: AnomaliesAPI): NonNullable<AreaAnalyticsFeatureProperties['anomalies']> =>\n parseMetricFields(api, (anomalies) => anomalies.map(parseAnomaly));\n\nconst parseFeatureProperties = (api: FeaturePropertiesAPI): AreaAnalyticsFeatureProperties => ({\n ...(api.name !== undefined && { name: api.name }),\n ...(api.timezone !== undefined && { timezone: api.timezone }),\n level: api.level,\n baseData: parseMetrics(api.baseData),\n timedData: parseTimedData(api.timedData),\n ...(api.tiledData && {\n tiledData: { tiles: api.tiledData.tiles.map(parseTileEntry) },\n }),\n ...(api.anomalies && {\n anomalies: parseAnomalies(api.anomalies),\n }),\n});\n\nconst parseFeature = (apiFeature: AreaAnalyticsFeatureAPI): AreaAnalyticsFeature => ({\n type: apiFeature.type,\n id: apiFeature.id,\n geometry: apiFeature.geometry,\n properties: parseFeatureProperties(apiFeature.properties),\n bbox: bboxFromGeoJSON(apiFeature.geometry) as BBox,\n});\n\nconst computeTileRanges = (features: AreaAnalyticsFeature[]): ByMetricKey<AreaAnalyticsMetricRange> => {\n const tiles = features.flatMap((f) => f.properties.tiledData?.tiles ?? []);\n const ranges: ByMetricKey<AreaAnalyticsMetricRange> = {};\n for (const key of areaAnalyticsMetricKeys) {\n const values = tiles.map((t) => t[key]).filter((v): v is number => v != null);\n if (values.length > 0) {\n ranges[key] = { min: Math.min(...values), max: Math.max(...values) };\n }\n }\n return ranges;\n};\n\n// The earliest and latest day, not the first and last: `days` comes in no guaranteed order.\nconst parseAnalysisPeriod = (\n days: AreaAnalyticsCollectionPropertiesAPI['days'],\n): Pick<AreaAnalyticsCollectionProperties, 'startDate' | 'endDate'> => {\n const times = days.map(startOfDayUTC);\n return { startDate: new Date(Math.min(...times)), endDate: new Date(Math.max(...times)) };\n};\n\n/**\n * Parses a raw Traffic Area Analytics API response into the SDK representation.\n *\n * In addition to renaming abbreviated metric fields and converting date strings\n * to `Date` objects, this function computes data-driven min/max ranges from\n * all tile entries across every feature and attaches them to\n * `result.properties.ranges`.\n *\n * @param apiResponse - The raw Area Analytics API response.\n */\nexport const parseTrafficAreaAnalyticsResponse = (apiResponse: AreaAnalyticsResponseAPI): TrafficAreaAnalytics => {\n const features = apiResponse.features.map(parseFeature);\n\n return {\n type: 'FeatureCollection',\n properties: {\n ...parseAnalysisPeriod(apiResponse.properties.days),\n metrics: apiResponse.properties.dataTypes\n .map(parseMetricKey)\n .filter((metricKey) => metricKey !== undefined),\n heatmap: apiResponse.properties.heatmap,\n frcs: apiResponse.properties.frcs,\n ranges: computeTileRanges(features),\n },\n features,\n };\n};\n","import { areaAnalyticsMetricKeys } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { DEFAULT_END_DATE_DAYS_AGO, daysAgo, LATEST_DATE_DAYS_AGO, ONE_DAY_MS, startOfDayUTC } from './dates';\nimport { type AreaAnalyticsDateInput, functionalRoadClasses } from './types/trafficAreaAnalyticsParams';\n\nconst dateInputSchema = z.union([\n z.date(),\n z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"Date must be in 'YYYY-MM-DD' format\"),\n]);\n\nconst geometrySchema = z.union([\n z.object({\n type: z.literal('Polygon'),\n coordinates: z.array(z.array(z.array(z.number()))).min(1),\n }),\n z.object({\n type: z.literal('MultiPolygon'),\n coordinates: z.array(z.array(z.array(z.array(z.number())))).min(1),\n }),\n]);\n\nconst isAccepted = (date: AreaAnalyticsDateInput): boolean =>\n startOfDayUTC(date) <= startOfDayUTC(daysAgo(LATEST_DATE_DAYS_AGO));\n\n/**\n * @ignore\n */\nexport const trafficAreaAnalyticsRequestSchema = commonServiceRequestSchema\n .extend({\n name: z.string().optional(),\n startDate: dateInputSchema.optional(),\n endDate: dateInputSchema.optional(),\n days: z.array(dateInputSchema).min(1).optional(),\n metrics: z.union([z.array(z.enum(areaAnalyticsMetricKeys)).min(1), z.literal('all')]),\n functionalRoadClasses: z.union([z.array(z.enum(functionalRoadClasses)).min(1), z.literal('all')]),\n hours: z.union([z.array(z.number().int().min(0).max(23)).min(1), z.literal('all')]),\n geometry: geometrySchema,\n })\n .refine(\n (data) => {\n const hasDays = data.days !== undefined;\n const hasStart = data.startDate !== undefined;\n\n // Exactly one mode: either days OR startDate (+optional endDate)\n if (hasDays && hasStart) return false;\n if (!hasDays && !hasStart) return false;\n\n // Validate range for continuous range: startDate must be at least 1 day before endDate, within 31 days\n if (data.startDate !== undefined) {\n const endDate = data.endDate ?? daysAgo(DEFAULT_END_DATE_DAYS_AGO);\n const diffDays = (startOfDayUTC(endDate) - startOfDayUTC(data.startDate)) / ONE_DAY_MS;\n return diffDays >= 1 && diffDays <= 31;\n }\n\n return true;\n },\n {\n message:\n 'Provide either startDate (with optional endDate, at least 1 day apart and within 31 days) or days — but not both',\n },\n )\n .refine(\n (data) => {\n if (data.days !== undefined) {\n return data.days.every(isAccepted);\n }\n\n return data.endDate === undefined || isAccepted(data.endDate);\n },\n {\n message: `Dates must be at least ${LATEST_DATE_DAYS_AGO} days before today`,\n },\n );\n","import type { TrafficAreaAnalytics } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { buildTrafficAreaAnalyticsRequest } from './requestBuilder';\nimport { parseTrafficAreaAnalyticsResponse } from './responseParser';\nimport { trafficAreaAnalyticsRequestSchema } from './trafficAreaAnalyticsRequestSchema';\nimport type { AreaAnalyticsRequestBody, AreaAnalyticsResponseAPI } from './types/apiTypes';\nimport type { TrafficAreaAnalyticsParams } from './types/trafficAreaAnalyticsParams';\n\n/**\n * Traffic Area Analytics service template type.\n * @ignore\n */\nexport type TrafficAreaAnalyticsTemplate = ServiceTemplate<\n TrafficAreaAnalyticsParams,\n FetchInput<AreaAnalyticsRequestBody>,\n AreaAnalyticsResponseAPI,\n TrafficAreaAnalytics\n>;\n\n/**\n * Traffic Area Analytics service template main implementation.\n * @ignore\n */\nexport const trafficAreaAnalyticsTemplate: TrafficAreaAnalyticsTemplate = {\n requestValidation: { schema: trafficAreaAnalyticsRequestSchema },\n buildRequest: buildTrafficAreaAnalyticsRequest,\n sendRequest: fetchWith,\n parseResponse: parseTrafficAreaAnalyticsResponse,\n};\n","import type { TrafficAreaAnalytics } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { trafficAreaAnalyticsTemplate } from './trafficAreaAnalyticsTemplate';\nimport type { TrafficAreaAnalyticsParams } from './types/trafficAreaAnalyticsParams';\n\n/**\n * Run a synchronous traffic area analytics report for a polygon region.\n *\n * Submits a single Polygon region with a date range and requested metrics, and\n * receives the full analysis result immediately (no polling required).\n *\n * @remarks\n * **Access**: needs a [TomTom Move Portal](https://move.tomtom.com) API key, passed as `apiKey` on the call, and is\n * unavailable on a Freemium or Pay As You Grow (PAYG) basis: [request access from TomTom Sales](https://www.tomtom.com/contact-sales/?source_app=docs-portal&source_product=traffic-area-analytics).\n *\n * **Lite report constraints:**\n * - Only a single region (Polygon Feature) may be submitted per request.\n * - The date range (`startDate` → `endDate`) must not exceed 31 days.\n *\n * **Key data returned per region:**\n * - `baseData`: overall aggregated metrics for the full analysis period\n * - `timedData`: time-series breakdown at yearly / monthly / weekly / daily / hourly granularity, and an average\n * week, all in UTC\n * - `tiledData`: per-tile heatmap metrics (when available)\n * - `anomalies`: detected traffic anomalies keyed by camelCase metric key (e.g. `anomalies.speed`), empty since\n * the service detects them only over 35 days or more\n *\n * **Available metrics (`metrics`):**\n * - `speed` — average speed on the road network (km/h)\n * - `freeFlowSpeed` — average speed under uncongested conditions (km/h)\n * - `congestionLevel` — percentage increase in travel time above free-flow\n * - `travelTime` — average travel time per 10 km (minutes)\n * - `networkLength` — total length of road segments with data (meters)\n *\n * @param params - Traffic Area Analytics parameters\n *\n * @returns Promise resolving to a {@link TrafficAreaAnalytics} FeatureCollection\n *\n * @example\n * ```typescript\n * // Basic speed and congestion analysis for Amsterdam\n * const result = await trafficAreaAnalytics({\n * startDate: new Date('2024-08-06'),\n * endDate: new Date('2024-08-06'),\n * functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],\n * hours: [7, 8, 9, 17, 18],\n * metrics: ['speed', 'congestionLevel'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [\n * [[4.896128, 52.382402], [4.875701, 52.368459], [4.923611, 52.36341], [4.896128, 52.382402]]\n * ]\n * }\n * });\n *\n * const region = result.features[0];\n * console.log(region.properties.baseData.speed); // avg speed (km/h)\n * console.log(region.properties.baseData.congestionLevel); // congestion %\n * console.log(region.properties.timedData.hourly); // hourly breakdown\n * ```\n *\n * @example\n * ```typescript\n * // Full metrics over a multi-day period\n * const result = await trafficAreaAnalytics({\n * startDate: new Date('2024-08-01'),\n * endDate: new Date('2024-08-07'),\n * functionalRoadClasses: 'all',\n * hours: 'all',\n * metrics: ['speed', 'freeFlowSpeed', 'congestionLevel', 'travelTime', 'networkLength'],\n * geometry: {\n * type: 'Polygon',\n * coordinates: [[[4.89, 52.37], [4.91, 52.37], [4.91, 52.39], [4.89, 52.39], [4.89, 52.37]]]\n * }\n * });\n *\n * result.features[0].properties.timedData.daily?.forEach(day => {\n * console.log(day.date, day.congestionLevel);\n * });\n * ```\n *\n * @see [Area Analytics API Documentation](https://developer.tomtom.com/area-analytics/documentation/api/analysis-lite)\n * @see [Traffic Area Analytics Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/traffic/traffic-area-analytics)\n *\n * @group Traffic\n */\nexport const trafficAreaAnalytics = async (params: TrafficAreaAnalyticsParams): Promise<TrafficAreaAnalytics> =>\n callService(params, trafficAreaAnalyticsTemplate, 'TrafficAreaAnalytics');\n","import { bboxFromGeoJSON, type TomTomAPIHeaders } from '@tomtom-org/maps-sdk/core';\nimport { bbox } from '@turf/turf';\nimport type { FetchInput } from '../shared';\nimport { appendByJoiningParamValue, buildCommonServiceRequestHeaders } from '../shared/request/requestBuildingUtils';\nimport { toIconCategory } from '../shared/trafficIncidents';\nimport type { IncidentEventAPI, IncidentPropertiesAPI } from './types/apiTypes';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\nconst TRAFFIC_URL_PATH = '/maps/orbis/traffic/incidents/details';\n\ntype IncidentAttribute = Exclude<keyof IncidentPropertiesAPI, 'events'> | `events.${keyof IncidentEventAPI}`;\n\n// v2 returns only the fields `Attributes` names and rejects a bare `*` or an unknown name. Keyed on the\n// wire type, so a field added to it without being requested here fails to compile.\nconst INCIDENT_PROPERTIES: Record<IncidentAttribute, true> = {\n id: true,\n iconCategory: true,\n magnitudeOfDelay: true,\n 'events.description': true,\n 'events.code': true,\n 'events.iconCategory': true,\n startTime: true,\n endTime: true,\n from: true,\n to: true,\n lengthInMeters: true,\n delayInSeconds: true,\n roadNumbers: true,\n timeValidity: true,\n probabilityOfOccurrence: true,\n numberOfReports: true,\n lastReportTime: true,\n};\nconst DEFAULT_ATTRIBUTES = [\n 'incidents.type',\n 'incidents.geometry.type',\n 'incidents.geometry.coordinates',\n ...Object.keys(INCIDENT_PROPERTIES).map((property) => `incidents.properties.${property}`),\n].join(',');\n\nconst MAX_GET_IDS = 5;\n\ntype TrafficIncidentDetailsPostBody = { ids: string[] };\n\nconst buildUrlBasePath = (params: TrafficIncidentDetailsParams): string => `${params.commonBaseURL}${TRAFFIC_URL_PATH}`;\n\nconst appendOptionalParams = (urlParams: URLSearchParams, params: TrafficIncidentDetailsParams): void => {\n appendByJoiningParamValue(urlParams, 'iconCategories', params.categoryFilter?.map(toIconCategory));\n appendByJoiningParamValue(urlParams, 'timeValidity', params.timeValidityFilter);\n};\n\nconst buildHeaders = (params: TrafficIncidentDetailsParams): TomTomAPIHeaders => ({\n ...buildCommonServiceRequestHeaders(params),\n Attributes: DEFAULT_ATTRIBUTES,\n});\n\n/**\n * Default method for building a traffic incident details request from {@link TrafficIncidentDetailsParams}.\n * @param params The traffic incident details parameters, with global configuration already merged into them.\n */\nexport const buildTrafficIncidentDetailsRequest = (\n params: TrafficIncidentDetailsParams,\n): FetchInput<TrafficIncidentDetailsPostBody> => {\n const url = new URL(buildUrlBasePath(params));\n const urlParams = url.searchParams;\n appendOptionalParams(urlParams, params);\n const headers = buildHeaders(params);\n\n if (params.ids) {\n const { ids } = params;\n if (ids.length > MAX_GET_IDS) {\n return { method: 'POST', url, headers, data: { ids } };\n }\n\n urlParams.append('ids', ids.join(','));\n return { method: 'GET', url, headers };\n }\n\n // The API only takes a bounding box: a polygon query sends its box and the parser trims the rest. That box\n // reads every coordinate, where `bboxFromGeoJSON` would trust an embedded `bbox` and sample long rings.\n const resolvedBBox = params.polygon ? bbox(params.polygon, { recompute: true }) : bboxFromGeoJSON(params.bbox);\n if (resolvedBBox) {\n urlParams.append('bbox', resolvedBBox.join(','));\n }\n return { method: 'GET', url, headers };\n};\n","import type { TrafficIncident, TrafficIncidentDetails, TrafficIncidentEvent } from '@tomtom-org/maps-sdk/core';\nimport { bbox, booleanIntersects } from '@turf/turf';\nimport type { BBox, MultiPolygon, Polygon } from 'geojson';\nimport { toDelayMagnitude, toTrafficIncidentCategory } from '../shared/trafficIncidents';\nimport type { IncidentAPI, IncidentDetailsResponseAPI, IncidentEventAPI } from './types/apiTypes';\nimport type {\n TrafficIncidentDetailsByPolygonParams,\n TrafficIncidentDetailsParams,\n} from './types/trafficIncidentDetailsParams';\n\ntype QueryPolygon = TrafficIncidentDetailsByPolygonParams['polygon'];\n\nconst parseEvent = (apiEvent: IncidentEventAPI): TrafficIncidentEvent => ({\n description: apiEvent.description,\n code: apiEvent.code,\n category: toTrafficIncidentCategory(apiEvent.iconCategory),\n});\n\nconst parseIncident = (apiIncident: IncidentAPI): TrafficIncident => {\n const properties = apiIncident.properties;\n return {\n type: 'Feature',\n // Top-level id is what the SDK's event-state machinery uses to track per-feature\n // click/hover state (see `renderedRefId` in map's featureId.ts). promoteId: 'id' on the\n // source keeps rendered features pointing at the same identifier.\n id: properties.id,\n geometry: apiIncident.geometry,\n properties: {\n id: properties.id,\n category: toTrafficIncidentCategory(properties.iconCategory),\n magnitudeOfDelay: toDelayMagnitude(properties.magnitudeOfDelay),\n events: (properties.events ?? []).map(parseEvent),\n ...(properties.startTime && { startTime: new Date(properties.startTime) }),\n ...(properties.endTime && { endTime: new Date(properties.endTime) }),\n ...(properties.from && { from: properties.from }),\n ...(properties.to && { to: properties.to }),\n // `!= null` drops the wire `null` too, which MapLibre's numeric paint/filter expressions reject.\n ...(properties.lengthInMeters != null && { lengthInMeters: properties.lengthInMeters }),\n ...(properties.delayInSeconds != null && { delayInSeconds: properties.delayInSeconds }),\n ...(properties.roadNumbers && { roadNumbers: properties.roadNumbers }),\n timeValidity: properties.timeValidity,\n ...(properties.probabilityOfOccurrence && { probabilityOfOccurrence: properties.probabilityOfOccurrence }),\n ...(properties.numberOfReports != null && { numberOfReports: properties.numberOfReports }),\n ...(properties.lastReportTime && { lastReportTime: new Date(properties.lastReportTime) }),\n },\n };\n};\n\nconst toGeometries = (polygon: QueryPolygon): (Polygon | MultiPolygon)[] => {\n if (polygon.type === 'FeatureCollection') {\n return polygon.features.map((feature) => feature.geometry);\n }\n\n return [polygon.type === 'Feature' ? polygon.geometry : polygon];\n};\n\n// Turf's predicates trust an embedded `bbox`, so a stale one would drop incidents inside the polygon.\nconst withoutBBox = (geometry: Polygon | MultiPolygon): Polygon | MultiPolygon =>\n geometry.type === 'Polygon'\n ? { type: 'Polygon', coordinates: geometry.coordinates }\n : { type: 'MultiPolygon', coordinates: geometry.coordinates };\n\nconst bboxesIntersect = (first: BBox, second: BBox): boolean =>\n first[0] <= second[2] && second[0] <= first[2] && first[1] <= second[3] && second[1] <= first[3];\n\n// Intersection rather than containment, so a LineString incident crossing the boundary is kept. The bbox test\n// spares the per-vertex `booleanIntersects` for the parts an incident is nowhere near.\nconst incidentsIntersecting = (incidents: TrafficIncident[], polygon: QueryPolygon): TrafficIncident[] => {\n const parts = toGeometries(polygon)\n .map(withoutBBox)\n .map((geometry) => ({ geometry, bbox: bbox(geometry) }));\n return incidents.filter((incident) => {\n const incidentBBox = bbox(incident.geometry);\n return parts.some(\n (part) => bboxesIntersect(incidentBBox, part.bbox) && booleanIntersects(incident.geometry, part.geometry),\n );\n });\n};\n\n/**\n * Default method for parsing a traffic incident details API response.\n * @param apiResponse The raw Traffic Incident Details API response.\n * @param params The request parameters; a `polygon` query keeps only the incidents that intersect it.\n */\nexport const parseTrafficIncidentDetailsResponse = (\n apiResponse: IncidentDetailsResponseAPI,\n params?: TrafficIncidentDetailsParams,\n): TrafficIncidentDetails => {\n const incidents = (apiResponse.incidents ?? [])\n .filter((incident): incident is IncidentAPI => incident !== null)\n .map(parseIncident);\n return {\n type: 'FeatureCollection',\n features: params?.polygon ? incidentsIntersecting(incidents, params.polygon) : incidents,\n };\n};\n","import { trafficIncidentRequestCategories } from '@tomtom-org/maps-sdk/core';\nimport { z } from 'zod';\nimport { commonServiceRequestSchema } from '../shared/schema/commonParamsSchema';\nimport { hasBBoxSchema } from '../shared/schema/geometriesSchema';\nimport type { SchemaRefinement } from '../shared/types/validation';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\nconst positionSchema = z.array(z.number()).min(2, 'a position needs at least two coordinates');\nconst linearRingSchema = z.array(positionSchema).min(4, 'a linear ring needs at least four positions');\nconst polygonCoordinatesSchema = z.array(linearRingSchema).min(1, 'a Polygon needs at least one ring');\n\nconst polygonGeometrySchema = z.discriminatedUnion('type', [\n z.object({\n type: z.literal('Polygon'),\n coordinates: polygonCoordinatesSchema,\n }),\n z.object({\n type: z.literal('MultiPolygon'),\n coordinates: z.array(polygonCoordinatesSchema).min(1, 'a MultiPolygon needs at least one polygon'),\n }),\n]);\n\nconst polygonFeatureSchema = z.object({\n type: z.literal('Feature'),\n geometry: polygonGeometrySchema,\n id: z.union([z.string(), z.number()]).optional(),\n properties: z.unknown().optional(),\n});\n\n// Polygonal shapes only: the response is trimmed with turf predicates that reject any other geometry. An embedded\n// `bbox` is not validated because neither the request nor the trim reads it.\nconst polygonSchema = z.union([\n polygonGeometrySchema,\n polygonFeatureSchema,\n z.object({\n type: z.literal('FeatureCollection'),\n features: z.array(polygonFeatureSchema).min(1, 'a FeatureCollection polygon needs at least one feature'),\n }),\n]);\n\n/**\n * @ignore\n */\nexport const trafficIncidentDetailsRequestSchema = commonServiceRequestSchema.extend({\n bbox: hasBBoxSchema.optional(),\n polygon: polygonSchema.optional(),\n ids: z.array(z.string()).min(1, 'ids must contain at least one id').optional(),\n categoryFilter: z.array(z.enum(trafficIncidentRequestCategories)).optional(),\n timeValidityFilter: z.array(z.enum(['present', 'future'])).optional(),\n});\n\nconst oneQueryModeRefinement: SchemaRefinement<TrafficIncidentDetailsParams> = {\n check: (data) => [data.bbox, data.polygon, data.ids].filter(Boolean).length === 1,\n message: 'Provide exactly one of bbox, polygon or ids',\n};\n\n/**\n * @ignore\n */\nexport const trafficIncidentDetailsRequestValidationConfig = {\n schema: trafficIncidentDetailsRequestSchema,\n refinements: [oneQueryModeRefinement],\n};\n","import type { TrafficIncidentDetails } from '@tomtom-org/maps-sdk/core';\nimport type { FetchInput, ServiceTemplate } from '../shared';\nimport { fetchWith } from '../shared/fetch';\nimport { buildTrafficIncidentDetailsRequest } from './requestBuilder';\nimport { parseTrafficIncidentDetailsResponse } from './responseParser';\nimport { trafficIncidentDetailsRequestValidationConfig } from './trafficIncidentDetailsRequestSchema';\nimport type { IncidentDetailsResponseAPI } from './types/apiTypes';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\ntype TrafficIncidentDetailsPostBody = { ids: string[] };\n\n/**\n * Traffic Incident Details service template type.\n * @ignore\n */\nexport type TrafficIncidentDetailsTemplate = ServiceTemplate<\n TrafficIncidentDetailsParams,\n FetchInput<TrafficIncidentDetailsPostBody>,\n IncidentDetailsResponseAPI,\n TrafficIncidentDetails\n>;\n\n/**\n * Traffic Incident Details service template main implementation.\n * @ignore\n */\nexport const trafficIncidentDetailsTemplate: TrafficIncidentDetailsTemplate = {\n requestValidation: trafficIncidentDetailsRequestValidationConfig,\n apiVersion: 2,\n buildRequest: buildTrafficIncidentDetailsRequest,\n sendRequest: fetchWith,\n parseResponse: parseTrafficIncidentDetailsResponse,\n};\n","import type { TrafficIncidentDetails } from '@tomtom-org/maps-sdk/core';\nimport { callService } from '../shared/serviceTemplate';\nimport { trafficIncidentDetailsTemplate } from './trafficIncidentDetailsTemplate';\nimport type { TrafficIncidentDetailsParams } from './types/trafficIncidentDetailsParams';\n\n/**\n * Fetch detailed information about traffic incidents.\n *\n * Query incidents by a geographic bounding box, by a polygon, or by a list of incident IDs.\n * Results are GeoJSON Features whose geometry is a `Point` (localised incidents) or\n * a `LineString` (incidents spanning a stretch of road).\n *\n * @remarks\n * **Three query modes:**\n *\n * - **Bounding box** (`bbox`): Returns all incidents within the given area.\n * Maximum area is 10,000 km².\n * - **Polygon** (`polygon`): Returns the incidents that intersect a polygon, such as a city boundary.\n * The request covers the polygon's bounding box, and the response is trimmed to the polygon.\n * - **By IDs** (`ids`): Returns the specified incidents directly.\n * GET is used automatically for up to 5 IDs; POST for up to 100 IDs.\n *\n * **Key data provided per incident:**\n * - Category (`category`): type of incident (accident, roadworks, jam, etc.)\n * - Delay magnitude: severity of the delay (`minor`, `moderate`, `major`, …)\n * - Geometry: exact location or extent of the incident on the road network\n * - Time information: start/end times and whether the incident is `present` or `future`\n * - Events: one or more detailed event descriptions\n *\n * @param params - Traffic Incident Details parameters (bbox, polygon or ids)\n *\n * @returns Promise resolving to a {@link TrafficIncidentDetails} object containing the matching incidents\n *\n * @example\n * ```typescript\n * // Query by bounding box (Amsterdam area)\n * const result = await trafficIncidentDetails({\n * bbox: [4.728, 52.278, 5.080, 52.479]\n * });\n *\n * result.features.forEach(incident => {\n * console.log(incident.properties.category); // 'accident' | 'jam' | …\n * console.log(incident.properties.magnitudeOfDelay); // 'minor' | 'major' | …\n * console.log(incident.geometry); // Point or LineString\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Query within a city boundary rather than its bounding box\n * const place = await geocodeOne('Amsterdam');\n * const result = await trafficIncidentDetails({\n * polygon: await geometryData({ geometries: [place] })\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Query up to 5 specific incidents by ID (GET)\n * const result = await trafficIncidentDetails({\n * ids: ['incident-id-1', 'incident-id-2']\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Query many incident IDs — POST is used automatically when ids.length > 5\n * const result = await trafficIncidentDetails({\n * ids: largeIdArray\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Filter by category and include future incidents\n * const result = await trafficIncidentDetails({\n * bbox: [4.728, 52.278, 5.080, 52.479],\n * categoryFilter: ['accident', 'road-closed'],\n * timeValidityFilter: ['present', 'future']\n * });\n * ```\n *\n * @see [Incident Details API Documentation](https://docs.tomtom.com/traffic-api/documentation/tomtom-orbis-maps/v2/traffic-incidents/incident-details)\n * @see [Incident Details Guide](https://docs.tomtom.com/maps-sdk-js/guides/services/traffic/incident-details)\n *\n * @group Traffic\n */\nexport const trafficIncidentDetails = async (params: TrafficIncidentDetailsParams): Promise<TrafficIncidentDetails> =>\n callService(params, trafficIncidentDetailsTemplate, 'TrafficIncidentDetails');\n"],"mappings":"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ACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MACJC,GAAI,MA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"}