minotor 6.0.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,11 +1,11 @@
1
- # [6.0.0](https://github.com/aubryio/minotor/compare/v5.0.1...v6.0.0) (2025-09-19)
1
+ # [7.0.0](https://github.com/aubryio/minotor/compare/v6.0.0...v7.0.0) (2025-09-19)
2
2
 
3
3
 
4
4
  ### Performance Improvements
5
5
 
6
- * use arrays instead of maps for stops adjacency and service routes ([#25](https://github.com/aubryio/minotor/issues/25)) ([c803b4d](https://github.com/aubryio/minotor/commit/c803b4d60d7a421b889938d88fc6f316ae43de2f))
6
+ * optimize loops on the critical path ([#26](https://github.com/aubryio/minotor/issues/26)) ([8504930](https://github.com/aubryio/minotor/commit/85049308f7bbcf4d88f3dcfb47edd2511e9da80d))
7
7
 
8
8
 
9
9
  ### BREAKING CHANGES
10
10
 
11
- * Stops and timetable binary format was updated and is not compatible with the old ones.
11
+ * Timetable binary format was updated and is not compatible with the old one.
package/dist/cli.mjs CHANGED
@@ -16079,20 +16079,15 @@ const serializeLocationType = (locationType) => {
16079
16079
  */
16080
16080
  class StopsIndex {
16081
16081
  constructor(stops) {
16082
+ var _a;
16082
16083
  this.stops = stops;
16083
16084
  this.sourceStopsMap = new Map();
16084
- stops.forEach((stop, id) => {
16085
- this.sourceStopsMap.set(stop.sourceStopId, id);
16086
- });
16087
- this.textIndex = lt({
16088
- fields: ['name'],
16089
- storeFields: ['id'],
16090
- searchOptions: { prefix: true, fuzzy: 0.2 },
16091
- processTerm: generateAccentVariants,
16092
- });
16093
16085
  const stopsSet = new Map();
16094
- stops.forEach((stop, id) => {
16095
- var _a;
16086
+ this.stopPoints = [];
16087
+ for (let id = 0; id < stops.length; id++) {
16088
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
16089
+ const stop = stops[id];
16090
+ this.sourceStopsMap.set(stop.sourceStopId, id);
16096
16091
  const effectiveStopId = (_a = stop.parent) !== null && _a !== void 0 ? _a : id;
16097
16092
  if (!stopsSet.has(effectiveStopId)) {
16098
16093
  stopsSet.set(effectiveStopId, {
@@ -16101,22 +16096,26 @@ class StopsIndex {
16101
16096
  name: stop.parent ? this.stops[stop.parent].name : stop.name,
16102
16097
  });
16103
16098
  }
16099
+ if (stop.lat && stop.lon) {
16100
+ this.stopPoints.push({
16101
+ id: id,
16102
+ lat: stop.lat,
16103
+ lon: stop.lon,
16104
+ });
16105
+ }
16106
+ }
16107
+ this.textIndex = lt({
16108
+ fields: ['name'],
16109
+ storeFields: ['id'],
16110
+ searchOptions: { prefix: true, fuzzy: 0.2 },
16111
+ processTerm: generateAccentVariants,
16104
16112
  });
16105
16113
  const stopsArray = Array.from(stopsSet.values());
16106
16114
  q(this.textIndex, stopsArray);
16107
- this.stopPoints = this.stops
16108
- .filter((stop) => {
16109
- if (stop.lat && stop.lon)
16110
- return true;
16111
- return false;
16112
- })
16113
- .map((stop, id) => ({
16114
- id: id,
16115
- lat: stop.lat,
16116
- lon: stop.lon,
16117
- }));
16118
16115
  this.geoIndex = new KDBush(this.stopPoints.length);
16119
- for (const { lat, lon } of this.stopPoints) {
16116
+ for (let i = 0; i < this.stopPoints.length; i++) {
16117
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
16118
+ const { lat, lon } = this.stopPoints[i];
16120
16119
  this.geoIndex.add(lon, lat);
16121
16120
  }
16122
16121
  this.geoIndex.finish();
@@ -16629,7 +16628,7 @@ const StopAdjacency = {
16629
16628
  }
16630
16629
  writer.uint32(18).fork();
16631
16630
  for (const v of message.routes) {
16632
- writer.int32(v);
16631
+ writer.uint32(v);
16633
16632
  }
16634
16633
  writer.join();
16635
16634
  return writer;
@@ -16650,13 +16649,13 @@ const StopAdjacency = {
16650
16649
  }
16651
16650
  case 2: {
16652
16651
  if (tag === 16) {
16653
- message.routes.push(reader.int32());
16652
+ message.routes.push(reader.uint32());
16654
16653
  continue;
16655
16654
  }
16656
16655
  if (tag === 18) {
16657
16656
  const end2 = reader.uint32() + reader.pos;
16658
16657
  while (reader.pos < end2) {
16659
- message.routes.push(reader.int32());
16658
+ message.routes.push(reader.uint32());
16660
16659
  }
16661
16660
  continue;
16662
16661
  }
@@ -16713,7 +16712,7 @@ const ServiceRoute = {
16713
16712
  }
16714
16713
  writer.uint32(26).fork();
16715
16714
  for (const v of message.routes) {
16716
- writer.int32(v);
16715
+ writer.uint32(v);
16717
16716
  }
16718
16717
  writer.join();
16719
16718
  return writer;
@@ -16741,13 +16740,13 @@ const ServiceRoute = {
16741
16740
  }
16742
16741
  case 3: {
16743
16742
  if (tag === 24) {
16744
- message.routes.push(reader.int32());
16743
+ message.routes.push(reader.uint32());
16745
16744
  continue;
16746
16745
  }
16747
16746
  if (tag === 26) {
16748
16747
  const end2 = reader.uint32() + reader.pos;
16749
16748
  while (reader.pos < end2) {
16750
- message.routes.push(reader.int32());
16749
+ message.routes.push(reader.uint32());
16751
16750
  }
16752
16751
  continue;
16753
16752
  }
@@ -17302,27 +17301,6 @@ let Route$1 = class Route {
17302
17301
  : byte & 0x03; // Lower 2 bits for first pair
17303
17302
  return toPickupDropOffType(dropOffValue);
17304
17303
  }
17305
- /**
17306
- * Iterates over the stops in the route, starting from an optional specified stop.
17307
- * If no start stop is provided, the iteration begins from the first stop in the route.
17308
- *
17309
- * @param [startStopId] - (Optional) The StopId of the stop to start the iteration from.
17310
- * @returns An IterableIterator of StopIds, starting from the specified stop or the first stop.
17311
- * @throws An error if the specified start stop is not found in the route.
17312
- */
17313
- stopsIterator(startStopId) {
17314
- const startIndex = startStopId !== undefined ? this.stopIndices.get(startStopId) : 0;
17315
- if (startIndex === undefined) {
17316
- throw new Error(`Start stop ${startStopId} not found in route ${this.serviceRouteId}`);
17317
- }
17318
- function* generator(stops, startIndex) {
17319
- for (let i = startIndex; i < stops.length; i++) {
17320
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
17321
- yield stops[i];
17322
- }
17323
- }
17324
- return generator(this.stops, startIndex);
17325
- }
17326
17304
  /**
17327
17305
  * Finds the earliest trip that can be taken from a specific stop on a given route,
17328
17306
  * optionally constrained by a latest trip index and a time before which the trip
@@ -17472,31 +17450,48 @@ const serializeServiceRoutesMap = (serviceRoutes) => {
17472
17450
  });
17473
17451
  };
17474
17452
  const deserializeStopsAdjacency = (protoStopsAdjacency) => {
17475
- return protoStopsAdjacency.map((value) => {
17476
- return {
17477
- transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: parseTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
17453
+ const result = [];
17454
+ for (let i = 0; i < protoStopsAdjacency.length; i++) {
17455
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
17456
+ const value = protoStopsAdjacency[i];
17457
+ const transfers = [];
17458
+ for (let j = 0; j < value.transfers.length; j++) {
17459
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
17460
+ const transfer = value.transfers[j];
17461
+ const newTransfer = Object.assign({ destination: transfer.destination, type: parseTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
17478
17462
  minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
17479
- })))),
17463
+ }));
17464
+ transfers.push(newTransfer);
17465
+ }
17466
+ result.push({
17467
+ transfers: transfers,
17480
17468
  routes: value.routes,
17481
- };
17482
- });
17469
+ });
17470
+ }
17471
+ return result;
17483
17472
  };
17484
17473
  const deserializeRoutesAdjacency = (protoRoutesAdjacency) => {
17485
17474
  const routesAdjacency = [];
17486
- protoRoutesAdjacency.forEach((value) => {
17475
+ for (let i = 0; i < protoRoutesAdjacency.length; i++) {
17476
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
17477
+ const value = protoRoutesAdjacency[i];
17487
17478
  const stops = bytesToUint32Array(value.stops);
17488
17479
  routesAdjacency.push(new Route$1(bytesToUint16Array(value.stopTimes), value.pickUpDropOffTypes, stops, value.serviceRouteId));
17489
- });
17480
+ }
17490
17481
  return routesAdjacency;
17491
17482
  };
17492
17483
  const deserializeServiceRoutesMap = (protoServiceRoutes) => {
17493
- return protoServiceRoutes.map((value) => {
17494
- return {
17484
+ const result = [];
17485
+ for (let i = 0; i < protoServiceRoutes.length; i++) {
17486
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
17487
+ const value = protoServiceRoutes[i];
17488
+ result.push({
17495
17489
  type: parseRouteType(value.type),
17496
17490
  name: value.name,
17497
17491
  routes: value.routes,
17498
- };
17499
- });
17492
+ });
17493
+ }
17494
+ return result;
17500
17495
  };
17501
17496
  const parseTransferType = (type) => {
17502
17497
  switch (type) {
@@ -17588,7 +17583,7 @@ const ALL_TRANSPORT_MODES = new Set([
17588
17583
  'TROLLEYBUS',
17589
17584
  'MONORAIL',
17590
17585
  ]);
17591
- const CURRENT_VERSION = '0.0.6';
17586
+ const CURRENT_VERSION = '0.0.7';
17592
17587
  /**
17593
17588
  * The internal transit timetable format.
17594
17589
  */
@@ -17696,7 +17691,8 @@ class Timetable {
17696
17691
  return [];
17697
17692
  }
17698
17693
  const routes = [];
17699
- for (const routeId of stopData.routes) {
17694
+ for (let i = 0; i < stopData.routes.length; i++) {
17695
+ const routeId = stopData.routes[i];
17700
17696
  const route = this.routesAdjacency[routeId];
17701
17697
  if (route) {
17702
17698
  routes.push(route);
@@ -17715,12 +17711,15 @@ class Timetable {
17715
17711
  */
17716
17712
  findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
17717
17713
  const reachableRoutes = new Map();
17718
- for (const originStop of fromStops) {
17714
+ const fromStopsArray = Array.from(fromStops);
17715
+ for (let i = 0; i < fromStopsArray.length; i++) {
17716
+ const originStop = fromStopsArray[i];
17719
17717
  const validRoutes = this.routesPassingThrough(originStop).filter((route) => {
17720
17718
  const serviceRoute = this.getServiceRouteInfo(route);
17721
17719
  return transportModes.has(serviceRoute.type);
17722
17720
  });
17723
- for (const route of validRoutes) {
17721
+ for (let j = 0; j < validRoutes.length; j++) {
17722
+ const route = validRoutes[j];
17724
17723
  const hopOnStop = reachableRoutes.get(route);
17725
17724
  if (hopOnStop) {
17726
17725
  if (route.isBefore(originStop, hopOnStop)) {
@@ -20135,8 +20134,12 @@ const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbSto
20135
20134
  for (let i = 0; i < nbStops; i++) {
20136
20135
  stopsAdjacency[i] = { routes: [], transfers: [] };
20137
20136
  }
20138
- routes.forEach((route, index) => {
20139
- for (const stop of route.stopsIterator()) {
20137
+ for (let index = 0; index < routes.length; index++) {
20138
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20139
+ const route = routes[index];
20140
+ for (let j = 0; j < route.getNbStops(); j++) {
20141
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20142
+ const stop = route.stops[j];
20140
20143
  if (activeStops.has(stop)) {
20141
20144
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20142
20145
  stopsAdjacency[stop].routes.push(index);
@@ -20147,9 +20150,11 @@ const buildStopsAdjacencyStructure = (serviceRoutes, routes, transfersMap, nbSto
20147
20150
  throw new Error(`Service route ${route.serviceRoute()} not found for route ${index}.`);
20148
20151
  }
20149
20152
  serviceRoute.routes.push(index);
20150
- });
20153
+ }
20151
20154
  for (const [stop, transfers] of transfersMap) {
20152
- for (const transfer of transfers) {
20155
+ for (let i = 0; i < transfers.length; i++) {
20156
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20157
+ const transfer = transfers[i];
20153
20158
  if (activeStops.has(stop) || activeStops.has(transfer.destination)) {
20154
20159
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20155
20160
  stopsAdjacency[stop].transfers.push(transfer);
@@ -20815,7 +20820,9 @@ class Router {
20815
20820
  var _a, _b;
20816
20821
  const { options } = query;
20817
20822
  const newlyMarkedStops = new Set();
20818
- for (const stop of markedStops) {
20823
+ const markedStopsArray = Array.from(markedStops);
20824
+ for (let i = 0; i < markedStopsArray.length; i++) {
20825
+ const stop = markedStopsArray[i];
20819
20826
  const currentArrival = arrivalsAtCurrentRound.get(stop);
20820
20827
  if (!currentArrival)
20821
20828
  continue;
@@ -20824,7 +20831,9 @@ class Router {
20824
20831
  if (previousLeg && !('route' in previousLeg)) {
20825
20832
  continue;
20826
20833
  }
20827
- for (const transfer of this.timetable.getTransfers(stop)) {
20834
+ const transfers = this.timetable.getTransfers(stop);
20835
+ for (let j = 0; j < transfers.length; j++) {
20836
+ const transfer = transfers[j];
20828
20837
  let transferTime;
20829
20838
  if (transfer.minTransferTime) {
20830
20839
  transferTime = transfer.minTransferTime;
@@ -20859,7 +20868,9 @@ class Router {
20859
20868
  }
20860
20869
  }
20861
20870
  }
20862
- for (const newStop of newlyMarkedStops) {
20871
+ const newlyMarkedStopsArray = Array.from(newlyMarkedStops);
20872
+ for (let i = 0; i < newlyMarkedStopsArray.length; i++) {
20873
+ const newStop = newlyMarkedStopsArray[i];
20863
20874
  markedStops.add(newStop);
20864
20875
  }
20865
20876
  }
@@ -20873,7 +20884,8 @@ class Router {
20873
20884
  earliestArrivalAtAnyStop(earliestArrivals, destinations) {
20874
20885
  var _a, _b;
20875
20886
  let earliestArrivalAtAnyDestination = UNREACHED;
20876
- for (const destination of destinations) {
20887
+ for (let i = 0; i < destinations.length; i++) {
20888
+ const destination = destinations[i];
20877
20889
  const arrival = (_b = (_a = earliestArrivals.get(destination.id)) === null || _a === void 0 ? void 0 : _a.arrival) !== null && _b !== void 0 ? _b : UNREACHED;
20878
20890
  earliestArrivalAtAnyDestination = Time.min(earliestArrivalAtAnyDestination, arrival);
20879
20891
  }
@@ -20897,7 +20909,8 @@ class Router {
20897
20909
  const earliestArrivalsPerRound = [earliestArrivalsWithoutAnyLeg];
20898
20910
  // Stops that have been improved at round k-1
20899
20911
  const markedStops = new Set();
20900
- for (const originStop of origins) {
20912
+ for (let i = 0; i < origins.length; i++) {
20913
+ const originStop = origins[i];
20901
20914
  markedStops.add(originStop.id);
20902
20915
  earliestArrivals.set(originStop.id, {
20903
20916
  arrival: departureTime,
@@ -20921,9 +20934,13 @@ class Router {
20921
20934
  const reachableRoutes = this.timetable.findReachableRoutes(markedStops, options.transportModes);
20922
20935
  markedStops.clear();
20923
20936
  // for each route that can be reached with at least round - 1 trips
20924
- for (const [route, hopOnStop] of reachableRoutes.entries()) {
20937
+ const reachableRoutesArray = Array.from(reachableRoutes.entries());
20938
+ for (let i = 0; i < reachableRoutesArray.length; i++) {
20939
+ const [route, hopOnStop] = reachableRoutesArray[i];
20925
20940
  let currentTrip = undefined;
20926
- for (const currentStop of route.stopsIterator(hopOnStop)) {
20941
+ const startIndex = route.stopIndex(hopOnStop);
20942
+ for (let j = startIndex; j < route.getNbStops(); j++) {
20943
+ const currentStop = route.stops[j];
20927
20944
  // If we're currently on a trip,
20928
20945
  // check if arrival at the stop improves the earliest arrival time
20929
20946
  if (currentTrip !== undefined) {