minotor 3.0.1 → 4.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.
Files changed (82) hide show
  1. package/.cspell.json +12 -1
  2. package/.gitattributes +3 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +3 -0
  4. package/.github/workflows/minotor.yml +17 -1
  5. package/CHANGELOG.md +8 -3
  6. package/README.md +35 -14
  7. package/dist/__e2e__/router.test.d.ts +1 -0
  8. package/dist/cli/perf.d.ts +28 -0
  9. package/dist/cli/utils.d.ts +6 -2
  10. package/dist/cli.mjs +2130 -909
  11. package/dist/cli.mjs.map +1 -1
  12. package/dist/gtfs/trips.d.ts +7 -1
  13. package/dist/gtfs/utils.d.ts +1 -1
  14. package/dist/parser.cjs.js +1236 -755
  15. package/dist/parser.cjs.js.map +1 -1
  16. package/dist/parser.d.ts +4 -2
  17. package/dist/parser.esm.js +1236 -755
  18. package/dist/parser.esm.js.map +1 -1
  19. package/dist/router.cjs.js +1 -1
  20. package/dist/router.cjs.js.map +1 -1
  21. package/dist/router.d.ts +10 -5
  22. package/dist/router.esm.js +1 -1
  23. package/dist/router.esm.js.map +1 -1
  24. package/dist/router.umd.js +1 -1
  25. package/dist/router.umd.js.map +1 -1
  26. package/dist/routing/__tests__/result.test.d.ts +1 -0
  27. package/dist/routing/query.d.ts +27 -6
  28. package/dist/routing/result.d.ts +1 -1
  29. package/dist/routing/route.d.ts +47 -2
  30. package/dist/routing/router.d.ts +15 -1
  31. package/dist/stops/stopsIndex.d.ts +3 -3
  32. package/dist/timetable/__tests__/route.test.d.ts +1 -0
  33. package/dist/timetable/__tests__/time.test.d.ts +1 -0
  34. package/dist/timetable/io.d.ts +7 -1
  35. package/dist/timetable/proto/timetable.d.ts +3 -2
  36. package/dist/timetable/route.d.ts +157 -0
  37. package/dist/timetable/time.d.ts +21 -0
  38. package/dist/timetable/timetable.d.ts +42 -62
  39. package/package.json +36 -34
  40. package/src/__e2e__/benchmark.json +22 -0
  41. package/src/__e2e__/router.test.ts +209 -0
  42. package/src/__e2e__/timetable/stops.bin +3 -0
  43. package/src/__e2e__/timetable/timetable.bin +3 -0
  44. package/src/cli/minotor.ts +51 -1
  45. package/src/cli/perf.ts +136 -0
  46. package/src/cli/repl.ts +25 -13
  47. package/src/cli/utils.ts +6 -28
  48. package/src/gtfs/__tests__/parser.test.ts +12 -15
  49. package/src/gtfs/__tests__/services.test.ts +1 -0
  50. package/src/gtfs/__tests__/transfers.test.ts +0 -1
  51. package/src/gtfs/__tests__/trips.test.ts +56 -74
  52. package/src/gtfs/parser.ts +49 -9
  53. package/src/gtfs/profiles/ch.ts +1 -1
  54. package/src/gtfs/routes.ts +4 -4
  55. package/src/gtfs/services.ts +15 -2
  56. package/src/gtfs/stops.ts +7 -3
  57. package/src/gtfs/transfers.ts +6 -3
  58. package/src/gtfs/trips.ts +206 -108
  59. package/src/gtfs/utils.ts +13 -2
  60. package/src/parser.ts +4 -2
  61. package/src/router.ts +17 -11
  62. package/src/routing/__tests__/result.test.ts +392 -0
  63. package/src/routing/__tests__/router.test.ts +94 -137
  64. package/src/routing/query.ts +28 -7
  65. package/src/routing/result.ts +10 -5
  66. package/src/routing/route.ts +95 -9
  67. package/src/routing/router.ts +82 -66
  68. package/src/stops/__tests__/io.test.ts +1 -1
  69. package/src/stops/__tests__/stopFinder.test.ts +1 -1
  70. package/src/stops/proto/stops.ts +4 -4
  71. package/src/stops/stopsIndex.ts +3 -3
  72. package/src/timetable/__tests__/io.test.ts +16 -23
  73. package/src/timetable/__tests__/route.test.ts +325 -0
  74. package/src/timetable/__tests__/time.test.ts +494 -0
  75. package/src/timetable/__tests__/timetable.test.ts +60 -75
  76. package/src/timetable/io.ts +32 -26
  77. package/src/timetable/proto/timetable.proto +3 -2
  78. package/src/timetable/proto/timetable.ts +15 -14
  79. package/src/timetable/route.ts +361 -0
  80. package/src/timetable/time.ts +40 -8
  81. package/src/timetable/timetable.ts +75 -166
  82. package/tsconfig.build.json +1 -1
@@ -37,7 +37,7 @@ function __values(o) {
37
37
  if (m) return m.call(o);
38
38
  if (o && typeof o.length === "number") return {
39
39
  next: function () {
40
- if (o && i >= o.length) o = undefined;
40
+ if (o && i >= o.length) o = void 0;
41
41
  return { value: o && o[i++], done: !o };
42
42
  }
43
43
  };
@@ -825,12 +825,13 @@ class SystemZone extends Zone {
825
825
  }
826
826
  }
827
827
 
828
- let dtfCache = {};
829
- function makeDTF(zone) {
830
- if (!dtfCache[zone]) {
831
- dtfCache[zone] = new Intl.DateTimeFormat("en-US", {
828
+ const dtfCache = new Map();
829
+ function makeDTF(zoneName) {
830
+ let dtf = dtfCache.get(zoneName);
831
+ if (dtf === undefined) {
832
+ dtf = new Intl.DateTimeFormat("en-US", {
832
833
  hour12: false,
833
- timeZone: zone,
834
+ timeZone: zoneName,
834
835
  year: "numeric",
835
836
  month: "2-digit",
836
837
  day: "2-digit",
@@ -839,8 +840,9 @@ function makeDTF(zone) {
839
840
  second: "2-digit",
840
841
  era: "short",
841
842
  });
843
+ dtfCache.set(zoneName, dtf);
842
844
  }
843
- return dtfCache[zone];
845
+ return dtf;
844
846
  }
845
847
 
846
848
  const typeToPos = {
@@ -876,7 +878,7 @@ function partsOffset(dtf, date) {
876
878
  return filled;
877
879
  }
878
880
 
879
- let ianaZoneCache = {};
881
+ const ianaZoneCache = new Map();
880
882
  /**
881
883
  * A zone identified by an IANA identifier, like America/New_York
882
884
  * @implements {Zone}
@@ -887,10 +889,11 @@ class IANAZone extends Zone {
887
889
  * @return {IANAZone}
888
890
  */
889
891
  static create(name) {
890
- if (!ianaZoneCache[name]) {
891
- ianaZoneCache[name] = new IANAZone(name);
892
+ let zone = ianaZoneCache.get(name);
893
+ if (zone === undefined) {
894
+ ianaZoneCache.set(name, (zone = new IANAZone(name)));
892
895
  }
893
- return ianaZoneCache[name];
896
+ return zone;
894
897
  }
895
898
 
896
899
  /**
@@ -898,8 +901,8 @@ class IANAZone extends Zone {
898
901
  * @return {void}
899
902
  */
900
903
  static resetCache() {
901
- ianaZoneCache = {};
902
- dtfCache = {};
904
+ ianaZoneCache.clear();
905
+ dtfCache.clear();
903
906
  }
904
907
 
905
908
  /**
@@ -1002,6 +1005,7 @@ class IANAZone extends Zone {
1002
1005
  * @return {number}
1003
1006
  */
1004
1007
  offset(ts) {
1008
+ if (!this.valid) return NaN;
1005
1009
  const date = new Date(ts);
1006
1010
 
1007
1011
  if (isNaN(date)) return NaN;
@@ -1067,36 +1071,36 @@ function getCachedLF(locString, opts = {}) {
1067
1071
  return dtf;
1068
1072
  }
1069
1073
 
1070
- let intlDTCache = {};
1074
+ const intlDTCache = new Map();
1071
1075
  function getCachedDTF(locString, opts = {}) {
1072
1076
  const key = JSON.stringify([locString, opts]);
1073
- let dtf = intlDTCache[key];
1074
- if (!dtf) {
1077
+ let dtf = intlDTCache.get(key);
1078
+ if (dtf === undefined) {
1075
1079
  dtf = new Intl.DateTimeFormat(locString, opts);
1076
- intlDTCache[key] = dtf;
1080
+ intlDTCache.set(key, dtf);
1077
1081
  }
1078
1082
  return dtf;
1079
1083
  }
1080
1084
 
1081
- let intlNumCache = {};
1085
+ const intlNumCache = new Map();
1082
1086
  function getCachedINF(locString, opts = {}) {
1083
1087
  const key = JSON.stringify([locString, opts]);
1084
- let inf = intlNumCache[key];
1085
- if (!inf) {
1088
+ let inf = intlNumCache.get(key);
1089
+ if (inf === undefined) {
1086
1090
  inf = new Intl.NumberFormat(locString, opts);
1087
- intlNumCache[key] = inf;
1091
+ intlNumCache.set(key, inf);
1088
1092
  }
1089
1093
  return inf;
1090
1094
  }
1091
1095
 
1092
- let intlRelCache = {};
1096
+ const intlRelCache = new Map();
1093
1097
  function getCachedRTF(locString, opts = {}) {
1094
1098
  const { base, ...cacheKeyOpts } = opts; // exclude `base` from the options
1095
1099
  const key = JSON.stringify([locString, cacheKeyOpts]);
1096
- let inf = intlRelCache[key];
1097
- if (!inf) {
1100
+ let inf = intlRelCache.get(key);
1101
+ if (inf === undefined) {
1098
1102
  inf = new Intl.RelativeTimeFormat(locString, opts);
1099
- intlRelCache[key] = inf;
1103
+ intlRelCache.set(key, inf);
1100
1104
  }
1101
1105
  return inf;
1102
1106
  }
@@ -1111,14 +1115,28 @@ function systemLocale() {
1111
1115
  }
1112
1116
  }
1113
1117
 
1114
- let weekInfoCache = {};
1118
+ const intlResolvedOptionsCache = new Map();
1119
+ function getCachedIntResolvedOptions(locString) {
1120
+ let opts = intlResolvedOptionsCache.get(locString);
1121
+ if (opts === undefined) {
1122
+ opts = new Intl.DateTimeFormat(locString).resolvedOptions();
1123
+ intlResolvedOptionsCache.set(locString, opts);
1124
+ }
1125
+ return opts;
1126
+ }
1127
+
1128
+ const weekInfoCache = new Map();
1115
1129
  function getCachedWeekInfo(locString) {
1116
- let data = weekInfoCache[locString];
1130
+ let data = weekInfoCache.get(locString);
1117
1131
  if (!data) {
1118
1132
  const locale = new Intl.Locale(locString);
1119
1133
  // browsers currently implement this as a property, but spec says it should be a getter function
1120
1134
  data = "getWeekInfo" in locale ? locale.getWeekInfo() : locale.weekInfo;
1121
- weekInfoCache[locString] = data;
1135
+ // minimalDays was removed from WeekInfo: https://github.com/tc39/proposal-intl-locale-info/issues/86
1136
+ if (!("minimalDays" in data)) {
1137
+ data = { ...fallbackWeekSettings, ...data };
1138
+ }
1139
+ weekInfoCache.set(locString, data);
1122
1140
  }
1123
1141
  return data;
1124
1142
  }
@@ -1217,7 +1235,7 @@ function supportsFastNumbers(loc) {
1217
1235
  loc.numberingSystem === "latn" ||
1218
1236
  !loc.locale ||
1219
1237
  loc.locale.startsWith("en") ||
1220
- new Intl.DateTimeFormat(loc.intl).resolvedOptions().numberingSystem === "latn"
1238
+ getCachedIntResolvedOptions(loc.locale).numberingSystem === "latn"
1221
1239
  );
1222
1240
  }
1223
1241
  }
@@ -1376,7 +1394,6 @@ const fallbackWeekSettings = {
1376
1394
  /**
1377
1395
  * @private
1378
1396
  */
1379
-
1380
1397
  class Locale {
1381
1398
  static fromOpts(opts) {
1382
1399
  return Locale.create(
@@ -1400,9 +1417,11 @@ class Locale {
1400
1417
 
1401
1418
  static resetCache() {
1402
1419
  sysLocaleCache = null;
1403
- intlDTCache = {};
1404
- intlNumCache = {};
1405
- intlRelCache = {};
1420
+ intlDTCache.clear();
1421
+ intlNumCache.clear();
1422
+ intlRelCache.clear();
1423
+ intlResolvedOptionsCache.clear();
1424
+ weekInfoCache.clear();
1406
1425
  }
1407
1426
 
1408
1427
  static fromObject({ locale, numberingSystem, outputCalendar, weekSettings } = {}) {
@@ -1467,10 +1486,18 @@ class Locale {
1467
1486
 
1468
1487
  months(length, format = false) {
1469
1488
  return listStuff(this, length, months, () => {
1489
+ // Workaround for "ja" locale: formatToParts does not label all parts of the month
1490
+ // as "month" and for this locale there is no difference between "format" and "non-format".
1491
+ // As such, just use format() instead of formatToParts() and take the whole string
1492
+ const monthSpecialCase = this.intl === "ja" || this.intl.startsWith("ja-");
1493
+ format &= !monthSpecialCase;
1470
1494
  const intl = format ? { month: length, day: "numeric" } : { month: length },
1471
1495
  formatStr = format ? "format" : "standalone";
1472
1496
  if (!this.monthsCache[formatStr][length]) {
1473
- this.monthsCache[formatStr][length] = mapMonths((dt) => this.extract(dt, intl, "month"));
1497
+ const mapper = !monthSpecialCase
1498
+ ? (dt) => this.extract(dt, intl, "month")
1499
+ : (dt) => this.dtFormatter(dt, intl).format();
1500
+ this.monthsCache[formatStr][length] = mapMonths(mapper);
1474
1501
  }
1475
1502
  return this.monthsCache[formatStr][length];
1476
1503
  });
@@ -1556,7 +1583,7 @@ class Locale {
1556
1583
  return (
1557
1584
  this.locale === "en" ||
1558
1585
  this.locale.toLowerCase() === "en-us" ||
1559
- new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith("en-us")
1586
+ getCachedIntResolvedOptions(this.intl).locale.startsWith("en-us")
1560
1587
  );
1561
1588
  }
1562
1589
 
@@ -1895,22 +1922,26 @@ function parseDigits(str) {
1895
1922
  }
1896
1923
 
1897
1924
  // cache of {numberingSystem: {append: regex}}
1898
- let digitRegexCache = {};
1925
+ const digitRegexCache = new Map();
1899
1926
  function resetDigitRegexCache() {
1900
- digitRegexCache = {};
1927
+ digitRegexCache.clear();
1901
1928
  }
1902
1929
 
1903
1930
  function digitRegex({ numberingSystem }, append = "") {
1904
1931
  const ns = numberingSystem || "latn";
1905
1932
 
1906
- if (!digitRegexCache[ns]) {
1907
- digitRegexCache[ns] = {};
1933
+ let appendCache = digitRegexCache.get(ns);
1934
+ if (appendCache === undefined) {
1935
+ appendCache = new Map();
1936
+ digitRegexCache.set(ns, appendCache);
1908
1937
  }
1909
- if (!digitRegexCache[ns][append]) {
1910
- digitRegexCache[ns][append] = new RegExp(`${numberingSystems[ns]}${append}`);
1938
+ let regex = appendCache.get(append);
1939
+ if (regex === undefined) {
1940
+ regex = new RegExp(`${numberingSystems[ns]}${append}`);
1941
+ appendCache.set(append, regex);
1911
1942
  }
1912
1943
 
1913
- return digitRegexCache[ns][append];
1944
+ return regex;
1914
1945
  }
1915
1946
 
1916
1947
  let now = () => Date.now(),
@@ -2452,10 +2483,24 @@ function parseMillis(fraction) {
2452
2483
  }
2453
2484
  }
2454
2485
 
2455
- function roundTo(number, digits, towardZero = false) {
2456
- const factor = 10 ** digits,
2457
- rounder = towardZero ? Math.trunc : Math.round;
2458
- return rounder(number * factor) / factor;
2486
+ function roundTo(number, digits, rounding = "round") {
2487
+ const factor = 10 ** digits;
2488
+ switch (rounding) {
2489
+ case "expand":
2490
+ return number > 0
2491
+ ? Math.ceil(number * factor) / factor
2492
+ : Math.floor(number * factor) / factor;
2493
+ case "trunc":
2494
+ return Math.trunc(number * factor) / factor;
2495
+ case "round":
2496
+ return Math.round(number * factor) / factor;
2497
+ case "floor":
2498
+ return Math.floor(number * factor) / factor;
2499
+ case "ceil":
2500
+ return Math.ceil(number * factor) / factor;
2501
+ default:
2502
+ throw new RangeError(`Value rounding ${rounding} is out of range`);
2503
+ }
2459
2504
  }
2460
2505
 
2461
2506
  // DATE BASICS
@@ -2563,7 +2608,7 @@ function signedOffset(offHourStr, offMinuteStr) {
2563
2608
 
2564
2609
  function asNumber(value) {
2565
2610
  const numericValue = Number(value);
2566
- if (typeof value === "boolean" || value === "" || Number.isNaN(numericValue))
2611
+ if (typeof value === "boolean" || value === "" || !Number.isFinite(numericValue))
2567
2612
  throw new InvalidArgumentError(`Invalid unit value ${value}`);
2568
2613
  return numericValue;
2569
2614
  }
@@ -2822,8 +2867,12 @@ class Formatter {
2822
2867
  for (let i = 0; i < fmt.length; i++) {
2823
2868
  const c = fmt.charAt(i);
2824
2869
  if (c === "'") {
2825
- if (currentFull.length > 0) {
2826
- splits.push({ literal: bracketed || /^\s+$/.test(currentFull), val: currentFull });
2870
+ // turn '' into a literal signal quote instead of just skipping the empty literal
2871
+ if (currentFull.length > 0 || bracketed) {
2872
+ splits.push({
2873
+ literal: bracketed || /^\s+$/.test(currentFull),
2874
+ val: currentFull === "" ? "'" : currentFull,
2875
+ });
2827
2876
  }
2828
2877
  current = null;
2829
2878
  currentFull = "";
@@ -2887,7 +2936,7 @@ class Formatter {
2887
2936
  return this.dtFormatter(dt, opts).resolvedOptions();
2888
2937
  }
2889
2938
 
2890
- num(n, p = 0) {
2939
+ num(n, p = 0, signDisplay = undefined) {
2891
2940
  // we get some perf out of doing this here, annoyingly
2892
2941
  if (this.opts.forceSimple) {
2893
2942
  return padStart(n, p);
@@ -2898,6 +2947,9 @@ class Formatter {
2898
2947
  if (p > 0) {
2899
2948
  opts.padTo = p;
2900
2949
  }
2950
+ if (signDisplay) {
2951
+ opts.signDisplay = signDisplay;
2952
+ }
2901
2953
 
2902
2954
  return this.loc.numberFormatter(opts).format(n);
2903
2955
  }
@@ -3133,32 +3185,44 @@ class Formatter {
3133
3185
  }
3134
3186
 
3135
3187
  formatDurationFromString(dur, fmt) {
3188
+ const invertLargest = this.opts.signMode === "negativeLargestOnly" ? -1 : 1;
3136
3189
  const tokenToField = (token) => {
3137
3190
  switch (token[0]) {
3138
3191
  case "S":
3139
- return "millisecond";
3192
+ return "milliseconds";
3140
3193
  case "s":
3141
- return "second";
3194
+ return "seconds";
3142
3195
  case "m":
3143
- return "minute";
3196
+ return "minutes";
3144
3197
  case "h":
3145
- return "hour";
3198
+ return "hours";
3146
3199
  case "d":
3147
- return "day";
3200
+ return "days";
3148
3201
  case "w":
3149
- return "week";
3202
+ return "weeks";
3150
3203
  case "M":
3151
- return "month";
3204
+ return "months";
3152
3205
  case "y":
3153
- return "year";
3206
+ return "years";
3154
3207
  default:
3155
3208
  return null;
3156
3209
  }
3157
3210
  },
3158
- tokenToString = (lildur) => (token) => {
3211
+ tokenToString = (lildur, info) => (token) => {
3159
3212
  const mapped = tokenToField(token);
3160
3213
  if (mapped) {
3161
- return this.num(lildur.get(mapped), token.length);
3214
+ const inversionFactor =
3215
+ info.isNegativeDuration && mapped !== info.largestUnit ? invertLargest : 1;
3216
+ let signDisplay;
3217
+ if (this.opts.signMode === "negativeLargestOnly" && mapped !== info.largestUnit) {
3218
+ signDisplay = "never";
3219
+ } else if (this.opts.signMode === "all") {
3220
+ signDisplay = "always";
3221
+ } else {
3222
+ // "auto" and "negative" are the same, but "auto" has better support
3223
+ signDisplay = "auto";
3224
+ }
3225
+ return this.num(lildur.get(mapped) * inversionFactor, token.length, signDisplay);
3162
3226
  } else {
3163
3227
  return token;
3164
3228
  }
@@ -3168,8 +3232,14 @@ class Formatter {
3168
3232
  (found, { literal, val }) => (literal ? found : found.concat(val)),
3169
3233
  []
3170
3234
  ),
3171
- collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t) => t));
3172
- return stringifyTokens(tokens, tokenToString(collapsed));
3235
+ collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t) => t)),
3236
+ durationInfo = {
3237
+ isNegativeDuration: collapsed < 0,
3238
+ // this relies on "collapsed" being based on "shiftTo", which builds up the object
3239
+ // in order
3240
+ largestUnit: Object.keys(collapsed.values)[0],
3241
+ };
3242
+ return stringifyTokens(tokens, tokenToString(collapsed, durationInfo));
3173
3243
  }
3174
3244
  }
3175
3245
 
@@ -3230,11 +3300,11 @@ function simpleParse(...keys) {
3230
3300
  }
3231
3301
 
3232
3302
  // ISO and SQL parsing
3233
- const offsetRegex = /(?:(Z)|([+-]\d\d)(?::?(\d\d))?)/;
3303
+ const offsetRegex = /(?:([Zz])|([+-]\d\d)(?::?(\d\d))?)/;
3234
3304
  const isoExtendedZone = `(?:${offsetRegex.source}?(?:\\[(${ianaRegex.source})\\])?)?`;
3235
3305
  const isoTimeBaseRegex = /(\d\d)(?::?(\d\d)(?::?(\d\d)(?:[.,](\d{1,30}))?)?)?/;
3236
3306
  const isoTimeRegex = RegExp(`${isoTimeBaseRegex.source}${isoExtendedZone}`);
3237
- const isoTimeExtensionRegex = RegExp(`(?:T${isoTimeRegex.source})?`);
3307
+ const isoTimeExtensionRegex = RegExp(`(?:[Tt]${isoTimeRegex.source})?`);
3238
3308
  const isoYmdRegex = /([+-]\d{6}|\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?/;
3239
3309
  const isoWeekRegex = /(\d{4})-?W(\d\d)(?:-?(\d))?/;
3240
3310
  const isoOrdinalRegex = /(\d{4})-?(\d{3})/;
@@ -3949,9 +4019,13 @@ let Duration$1 = class Duration {
3949
4019
  * @param {string} fmt - the format string
3950
4020
  * @param {Object} opts - options
3951
4021
  * @param {boolean} [opts.floor=true] - floor numerical values
4022
+ * @param {'negative'|'all'|'negativeLargestOnly'} [opts.signMode=negative] - How to handle signs
3952
4023
  * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("y d s") //=> "1 6 2"
3953
4024
  * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("yy dd sss") //=> "01 06 002"
3954
4025
  * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("M S") //=> "12 518402000"
4026
+ * @example Duration.fromObject({ days: 6, seconds: 2 }).toFormat("d s", { signMode: "all" }) //=> "+6 +2"
4027
+ * @example Duration.fromObject({ days: -6, seconds: -2 }).toFormat("d s", { signMode: "all" }) //=> "-6 -2"
4028
+ * @example Duration.fromObject({ days: -6, seconds: -2 }).toFormat("d s", { signMode: "negativeLargestOnly" }) //=> "-6 2"
3955
4029
  * @return {string}
3956
4030
  */
3957
4031
  toFormat(fmt, opts = {}) {
@@ -3971,21 +4045,25 @@ let Duration$1 = class Duration {
3971
4045
  * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options
3972
4046
  * @param {Object} opts - Formatting options. Accepts the same keys as the options parameter of the native `Intl.NumberFormat` constructor, as well as `listStyle`.
3973
4047
  * @param {string} [opts.listStyle='narrow'] - How to format the merged list. Corresponds to the `style` property of the options parameter of the native `Intl.ListFormat` constructor.
4048
+ * @param {boolean} [opts.showZeros=true] - Show all units previously used by the duration even if they are zero
3974
4049
  * @example
3975
4050
  * ```js
3976
- * var dur = Duration.fromObject({ days: 1, hours: 5, minutes: 6 })
3977
- * dur.toHuman() //=> '1 day, 5 hours, 6 minutes'
3978
- * dur.toHuman({ listStyle: "long" }) //=> '1 day, 5 hours, and 6 minutes'
3979
- * dur.toHuman({ unitDisplay: "short" }) //=> '1 day, 5 hr, 6 min'
4051
+ * var dur = Duration.fromObject({ months: 1, weeks: 0, hours: 5, minutes: 6 })
4052
+ * dur.toHuman() //=> '1 month, 0 weeks, 5 hours, 6 minutes'
4053
+ * dur.toHuman({ listStyle: "long" }) //=> '1 month, 0 weeks, 5 hours, and 6 minutes'
4054
+ * dur.toHuman({ unitDisplay: "short" }) //=> '1 mth, 0 wks, 5 hr, 6 min'
4055
+ * dur.toHuman({ showZeros: false }) //=> '1 month, 5 hours, 6 minutes'
3980
4056
  * ```
3981
4057
  */
3982
4058
  toHuman(opts = {}) {
3983
4059
  if (!this.isValid) return INVALID$2;
3984
4060
 
4061
+ const showZeros = opts.showZeros !== false;
4062
+
3985
4063
  const l = orderedUnits$1
3986
4064
  .map((unit) => {
3987
4065
  const val = this.values[unit];
3988
- if (isUndefined(val)) {
4066
+ if (isUndefined(val) || (val === 0 && !showZeros)) {
3989
4067
  return null;
3990
4068
  }
3991
4069
  return this.loc
@@ -4345,6 +4423,17 @@ let Duration$1 = class Duration {
4345
4423
  return clone$1(this, { values: negated }, true);
4346
4424
  }
4347
4425
 
4426
+ /**
4427
+ * Removes all units with values equal to 0 from this Duration.
4428
+ * @example Duration.fromObject({ years: 2, days: 0, hours: 0, minutes: 0 }).removeZeros().toObject() //=> { years: 2 }
4429
+ * @return {Duration}
4430
+ */
4431
+ removeZeros() {
4432
+ if (!this.isValid) return this;
4433
+ const vals = removeZeroes(this.values);
4434
+ return clone$1(this, { values: vals }, true);
4435
+ }
4436
+
4348
4437
  /**
4349
4438
  * Get the years.
4350
4439
  * @type {number}
@@ -4655,13 +4744,22 @@ class Interval {
4655
4744
  }
4656
4745
 
4657
4746
  /**
4658
- * Returns the end of the Interval
4747
+ * Returns the end of the Interval. This is the first instant which is not part of the interval
4748
+ * (Interval is half-open).
4659
4749
  * @type {DateTime}
4660
4750
  */
4661
4751
  get end() {
4662
4752
  return this.isValid ? this.e : null;
4663
4753
  }
4664
4754
 
4755
+ /**
4756
+ * Returns the last DateTime included in the interval (since end is not part of the interval)
4757
+ * @type {DateTime}
4758
+ */
4759
+ get lastDateTime() {
4760
+ return this.isValid ? (this.e ? this.e.minus(1) : null) : null;
4761
+ }
4762
+
4665
4763
  /**
4666
4764
  * Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'.
4667
4765
  * @type {boolean}
@@ -4926,8 +5024,11 @@ class Interval {
4926
5024
  }
4927
5025
 
4928
5026
  /**
4929
- * Merge an array of Intervals into a equivalent minimal set of Intervals.
5027
+ * Merge an array of Intervals into an equivalent minimal set of Intervals.
4930
5028
  * Combines overlapping and adjacent Intervals.
5029
+ * The resulting array will contain the Intervals in ascending order, that is, starting with the earliest Interval
5030
+ * and ending with the latest.
5031
+ *
4931
5032
  * @param {Array} intervals
4932
5033
  * @return {Array}
4933
5034
  */
@@ -6075,21 +6176,22 @@ function toTechFormat(dt, format, allowZ = true) {
6075
6176
  : null;
6076
6177
  }
6077
6178
 
6078
- function toISODate(o, extended) {
6179
+ function toISODate(o, extended, precision) {
6079
6180
  const longFormat = o.c.year > 9999 || o.c.year < 0;
6080
6181
  let c = "";
6081
6182
  if (longFormat && o.c.year >= 0) c += "+";
6082
6183
  c += padStart(o.c.year, longFormat ? 6 : 4);
6083
-
6184
+ if (precision === "year") return c;
6084
6185
  if (extended) {
6085
6186
  c += "-";
6086
6187
  c += padStart(o.c.month);
6188
+ if (precision === "month") return c;
6087
6189
  c += "-";
6088
- c += padStart(o.c.day);
6089
6190
  } else {
6090
6191
  c += padStart(o.c.month);
6091
- c += padStart(o.c.day);
6192
+ if (precision === "month") return c;
6092
6193
  }
6194
+ c += padStart(o.c.day);
6093
6195
  return c;
6094
6196
  }
6095
6197
 
@@ -6099,26 +6201,39 @@ function toISOTime(
6099
6201
  suppressSeconds,
6100
6202
  suppressMilliseconds,
6101
6203
  includeOffset,
6102
- extendedZone
6204
+ extendedZone,
6205
+ precision
6103
6206
  ) {
6104
- let c = padStart(o.c.hour);
6105
- if (extended) {
6106
- c += ":";
6107
- c += padStart(o.c.minute);
6108
- if (o.c.millisecond !== 0 || o.c.second !== 0 || !suppressSeconds) {
6109
- c += ":";
6110
- }
6111
- } else {
6112
- c += padStart(o.c.minute);
6113
- }
6114
-
6115
- if (o.c.millisecond !== 0 || o.c.second !== 0 || !suppressSeconds) {
6116
- c += padStart(o.c.second);
6117
-
6118
- if (o.c.millisecond !== 0 || !suppressMilliseconds) {
6119
- c += ".";
6120
- c += padStart(o.c.millisecond, 3);
6121
- }
6207
+ let showSeconds = !suppressSeconds || o.c.millisecond !== 0 || o.c.second !== 0,
6208
+ c = "";
6209
+ switch (precision) {
6210
+ case "day":
6211
+ case "month":
6212
+ case "year":
6213
+ break;
6214
+ default:
6215
+ c += padStart(o.c.hour);
6216
+ if (precision === "hour") break;
6217
+ if (extended) {
6218
+ c += ":";
6219
+ c += padStart(o.c.minute);
6220
+ if (precision === "minute") break;
6221
+ if (showSeconds) {
6222
+ c += ":";
6223
+ c += padStart(o.c.second);
6224
+ }
6225
+ } else {
6226
+ c += padStart(o.c.minute);
6227
+ if (precision === "minute") break;
6228
+ if (showSeconds) {
6229
+ c += padStart(o.c.second);
6230
+ }
6231
+ }
6232
+ if (precision === "second") break;
6233
+ if (showSeconds && (!suppressMilliseconds || o.c.millisecond !== 0)) {
6234
+ c += ".";
6235
+ c += padStart(o.c.millisecond, 3);
6236
+ }
6122
6237
  }
6123
6238
 
6124
6239
  if (includeOffset) {
@@ -6250,15 +6365,27 @@ function normalizeUnitWithLocalWeeks(unit) {
6250
6365
  // This is safe for quickDT (used by local() and utc()) because we don't fill in
6251
6366
  // higher-order units from tsNow (as we do in fromObject, this requires that
6252
6367
  // offset is calculated from tsNow).
6368
+ /**
6369
+ * @param {Zone} zone
6370
+ * @return {number}
6371
+ */
6253
6372
  function guessOffsetForZone(zone) {
6254
- if (!zoneOffsetGuessCache[zone]) {
6255
- if (zoneOffsetTs === undefined) {
6256
- zoneOffsetTs = Settings.now();
6257
- }
6373
+ if (zoneOffsetTs === undefined) {
6374
+ zoneOffsetTs = Settings.now();
6375
+ }
6258
6376
 
6259
- zoneOffsetGuessCache[zone] = zone.offset(zoneOffsetTs);
6377
+ // Do not cache anything but IANA zones, because it is not safe to do so.
6378
+ // Guessing an offset which is not present in the zone can cause wrong results from fixOffset
6379
+ if (zone.type !== "iana") {
6380
+ return zone.offset(zoneOffsetTs);
6260
6381
  }
6261
- return zoneOffsetGuessCache[zone];
6382
+ const zoneName = zone.name;
6383
+ let offsetGuess = zoneOffsetGuessCache.get(zoneName);
6384
+ if (offsetGuess === undefined) {
6385
+ offsetGuess = zone.offset(zoneOffsetTs);
6386
+ zoneOffsetGuessCache.set(zoneName, offsetGuess);
6387
+ }
6388
+ return offsetGuess;
6262
6389
  }
6263
6390
 
6264
6391
  // this is a dumbed down version of fromObject() that runs about 60% faster
@@ -6298,8 +6425,9 @@ function quickDT(obj, opts) {
6298
6425
 
6299
6426
  function diffRelative(start, end, opts) {
6300
6427
  const round = isUndefined(opts.round) ? true : opts.round,
6428
+ rounding = isUndefined(opts.rounding) ? "trunc" : opts.rounding,
6301
6429
  format = (c, unit) => {
6302
- c = roundTo(c, round || opts.calendary ? 0 : 2, true);
6430
+ c = roundTo(c, round || opts.calendary ? 0 : 2, opts.calendary ? "round" : rounding);
6303
6431
  const formatter = end.loc.clone(opts).relFormatter(opts);
6304
6432
  return formatter.format(c, unit);
6305
6433
  },
@@ -6348,7 +6476,7 @@ let zoneOffsetTs;
6348
6476
  * This optimizes quickDT via guessOffsetForZone to avoid repeated calls of
6349
6477
  * zone.offset().
6350
6478
  */
6351
- let zoneOffsetGuessCache = {};
6479
+ const zoneOffsetGuessCache = new Map();
6352
6480
 
6353
6481
  /**
6354
6482
  * A DateTime is an immutable data structure representing a specific date and time and accompanying methods. It contains class and instance methods for creating, parsing, interrogating, transforming, and formatting them.
@@ -6552,7 +6680,7 @@ class DateTime {
6552
6680
  throw new InvalidArgumentError(
6553
6681
  `fromMillis requires a numerical input, but received a ${typeof milliseconds} with value ${milliseconds}`
6554
6682
  );
6555
- } else if (milliseconds < -864e13 || milliseconds > MAX_DATE) {
6683
+ } else if (milliseconds < -MAX_DATE || milliseconds > MAX_DATE) {
6556
6684
  // this isn't perfect because we can still end up out of range because of additional shifting, but it's a start
6557
6685
  return DateTime.invalid("Timestamp out of range");
6558
6686
  } else {
@@ -6913,7 +7041,7 @@ class DateTime {
6913
7041
 
6914
7042
  static resetCache() {
6915
7043
  zoneOffsetTs = undefined;
6916
- zoneOffsetGuessCache = {};
7044
+ zoneOffsetGuessCache.clear();
6917
7045
  }
6918
7046
 
6919
7047
  // INFO
@@ -7678,11 +7806,14 @@ class DateTime {
7678
7806
  * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
7679
7807
  * @param {boolean} [opts.extendedZone=false] - add the time zone format extension
7680
7808
  * @param {string} [opts.format='extended'] - choose between the basic and extended format
7809
+ * @param {string} [opts.precision='milliseconds'] - truncate output to desired presicion: 'years', 'months', 'days', 'hours', 'minutes', 'seconds' or 'milliseconds'. When precision and suppressSeconds or suppressMilliseconds are used together, precision sets the maximum unit shown in the output, however seconds or milliseconds will still be suppressed if they are 0.
7681
7810
  * @example DateTime.utc(1983, 5, 25).toISO() //=> '1982-05-25T00:00:00.000Z'
7682
7811
  * @example DateTime.now().toISO() //=> '2017-04-22T20:47:05.335-04:00'
7683
7812
  * @example DateTime.now().toISO({ includeOffset: false }) //=> '2017-04-22T20:47:05.335'
7684
7813
  * @example DateTime.now().toISO({ format: 'basic' }) //=> '20170422T204705.335-0400'
7685
- * @return {string}
7814
+ * @example DateTime.now().toISO({ precision: 'day' }) //=> '2017-04-22Z'
7815
+ * @example DateTime.now().toISO({ precision: 'minute' }) //=> '2017-04-22T20:47Z'
7816
+ * @return {string|null}
7686
7817
  */
7687
7818
  toISO({
7688
7819
  format = "extended",
@@ -7690,16 +7821,26 @@ class DateTime {
7690
7821
  suppressMilliseconds = false,
7691
7822
  includeOffset = true,
7692
7823
  extendedZone = false,
7824
+ precision = "milliseconds",
7693
7825
  } = {}) {
7694
7826
  if (!this.isValid) {
7695
7827
  return null;
7696
7828
  }
7697
7829
 
7830
+ precision = normalizeUnit(precision);
7698
7831
  const ext = format === "extended";
7699
7832
 
7700
- let c = toISODate(this, ext);
7701
- c += "T";
7702
- c += toISOTime(this, ext, suppressSeconds, suppressMilliseconds, includeOffset, extendedZone);
7833
+ let c = toISODate(this, ext, precision);
7834
+ if (orderedUnits.indexOf(precision) >= 3) c += "T";
7835
+ c += toISOTime(
7836
+ this,
7837
+ ext,
7838
+ suppressSeconds,
7839
+ suppressMilliseconds,
7840
+ includeOffset,
7841
+ extendedZone,
7842
+ precision
7843
+ );
7703
7844
  return c;
7704
7845
  }
7705
7846
 
@@ -7707,16 +7848,17 @@ class DateTime {
7707
7848
  * Returns an ISO 8601-compliant string representation of this DateTime's date component
7708
7849
  * @param {Object} opts - options
7709
7850
  * @param {string} [opts.format='extended'] - choose between the basic and extended format
7851
+ * @param {string} [opts.precision='day'] - truncate output to desired precision: 'years', 'months', or 'days'.
7710
7852
  * @example DateTime.utc(1982, 5, 25).toISODate() //=> '1982-05-25'
7711
7853
  * @example DateTime.utc(1982, 5, 25).toISODate({ format: 'basic' }) //=> '19820525'
7712
- * @return {string}
7854
+ * @example DateTime.utc(1982, 5, 25).toISODate({ precision: 'month' }) //=> '1982-05'
7855
+ * @return {string|null}
7713
7856
  */
7714
- toISODate({ format = "extended" } = {}) {
7857
+ toISODate({ format = "extended", precision = "day" } = {}) {
7715
7858
  if (!this.isValid) {
7716
7859
  return null;
7717
7860
  }
7718
-
7719
- return toISODate(this, format === "extended");
7861
+ return toISODate(this, format === "extended", normalizeUnit(precision));
7720
7862
  }
7721
7863
 
7722
7864
  /**
@@ -7737,10 +7879,12 @@ class DateTime {
7737
7879
  * @param {boolean} [opts.extendedZone=true] - add the time zone format extension
7738
7880
  * @param {boolean} [opts.includePrefix=false] - include the `T` prefix
7739
7881
  * @param {string} [opts.format='extended'] - choose between the basic and extended format
7882
+ * @param {string} [opts.precision='milliseconds'] - truncate output to desired presicion: 'hours', 'minutes', 'seconds' or 'milliseconds'. When precision and suppressSeconds or suppressMilliseconds are used together, precision sets the maximum unit shown in the output, however seconds or milliseconds will still be suppressed if they are 0.
7740
7883
  * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime() //=> '07:34:19.361Z'
7741
7884
  * @example DateTime.utc().set({ hour: 7, minute: 34, seconds: 0, milliseconds: 0 }).toISOTime({ suppressSeconds: true }) //=> '07:34Z'
7742
7885
  * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ format: 'basic' }) //=> '073419.361Z'
7743
7886
  * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ includePrefix: true }) //=> 'T07:34:19.361Z'
7887
+ * @example DateTime.utc().set({ hour: 7, minute: 34, second: 56 }).toISOTime({ precision: 'minute' }) //=> '07:34Z'
7744
7888
  * @return {string}
7745
7889
  */
7746
7890
  toISOTime({
@@ -7750,12 +7894,14 @@ class DateTime {
7750
7894
  includePrefix = false,
7751
7895
  extendedZone = false,
7752
7896
  format = "extended",
7897
+ precision = "milliseconds",
7753
7898
  } = {}) {
7754
7899
  if (!this.isValid) {
7755
7900
  return null;
7756
7901
  }
7757
7902
 
7758
- let c = includePrefix ? "T" : "";
7903
+ precision = normalizeUnit(precision);
7904
+ let c = includePrefix && orderedUnits.indexOf(precision) >= 3 ? "T" : "";
7759
7905
  return (
7760
7906
  c +
7761
7907
  toISOTime(
@@ -7764,7 +7910,8 @@ class DateTime {
7764
7910
  suppressSeconds,
7765
7911
  suppressMilliseconds,
7766
7912
  includeOffset,
7767
- extendedZone
7913
+ extendedZone,
7914
+ precision
7768
7915
  )
7769
7916
  );
7770
7917
  }
@@ -7794,7 +7941,7 @@ class DateTime {
7794
7941
  /**
7795
7942
  * Returns a string representation of this DateTime appropriate for use in SQL Date
7796
7943
  * @example DateTime.utc(2014, 7, 13).toSQLDate() //=> '2014-07-13'
7797
- * @return {string}
7944
+ * @return {string|null}
7798
7945
  */
7799
7946
  toSQLDate() {
7800
7947
  if (!this.isValid) {
@@ -7889,7 +8036,7 @@ class DateTime {
7889
8036
  }
7890
8037
 
7891
8038
  /**
7892
- * Returns the epoch seconds of this DateTime.
8039
+ * Returns the epoch seconds (including milliseconds in the fractional part) of this DateTime.
7893
8040
  * @return {number}
7894
8041
  */
7895
8042
  toSeconds() {
@@ -7996,7 +8143,7 @@ class DateTime {
7996
8143
  /**
7997
8144
  * Return an Interval spanning between this DateTime and another DateTime
7998
8145
  * @param {DateTime} otherDateTime - the other end point of the Interval
7999
- * @return {Interval}
8146
+ * @return {Interval|DateTime}
8000
8147
  */
8001
8148
  until(otherDateTime) {
8002
8149
  return this.isValid ? Interval.fromDateTimes(this, otherDateTime) : this;
@@ -8042,12 +8189,13 @@ class DateTime {
8042
8189
 
8043
8190
  /**
8044
8191
  * Returns a string representation of a this time relative to now, such as "in two days". Can only internationalize if your
8045
- * platform supports Intl.RelativeTimeFormat. Rounds down by default.
8192
+ * platform supports Intl.RelativeTimeFormat. Rounds towards zero by default.
8046
8193
  * @param {Object} options - options that affect the output
8047
8194
  * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.
8048
8195
  * @param {string} [options.style="long"] - the style of units, must be "long", "short", or "narrow"
8049
8196
  * @param {string|string[]} options.unit - use a specific unit or array of units; if omitted, or an array, the method will pick the best unit. Use an array or one of "years", "quarters", "months", "weeks", "days", "hours", "minutes", or "seconds"
8050
8197
  * @param {boolean} [options.round=true] - whether to round the numbers in the output.
8198
+ * @param {string} [options.rounding="trunc"] - rounding method to use when rounding the numbers in the output. Can be "trunc" (toward zero), "expand" (away from zero), "round", "floor", or "ceil".
8051
8199
  * @param {number} [options.padding=0] - padding in milliseconds. This allows you to round up the result if it fits inside the threshold. Don't use in combination with {round: false} because the decimal output will include the padding.
8052
8200
  * @param {string} options.locale - override the locale of this DateTime
8053
8201
  * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this
@@ -10435,7 +10583,7 @@ class BinaryReader {
10435
10583
  // ignore
10436
10584
  }
10437
10585
  break;
10438
- // @ts-expect-error TS7029: Fallthrough case in switch
10586
+ // @ts-ignore TS7029: Fallthrough case in switch -- ignore instead of expect-error for compiler settings without noFallthroughCasesInSwitch: true
10439
10587
  case WireType.Bit64:
10440
10588
  this.pos += 4;
10441
10589
  case WireType.Bit32:
@@ -10716,7 +10864,6 @@ function around(index, lng, lat, maxResults = Infinity, maxDistance = Infinity,
10716
10864
  left: 0, // left index in the kd-tree array
10717
10865
  right: index.ids.length - 1, // right index
10718
10866
  axis: 0, // 0 for longitude axis and 1 for latitude axis
10719
- dist: 0, // will hold the lower bound of children's distances to the query point
10720
10867
  minLng: -180, // bounding box of the node
10721
10868
  minLat: -90,
10722
10869
  maxLng: 180,
@@ -11184,7 +11331,7 @@ function sqDist(ax, ay, bx, by) {
11184
11331
  return dx * dx + dy * dy;
11185
11332
  }
11186
11333
 
11187
- const xt="ENTRIES",B="KEYS",G="VALUES",g="";class V{set;_type;_path;constructor(e,n){const o=e._tree,s=Array.from(o.keys());this.set=e,this._type=n,this._path=s.length>0?[{node:o,keys:s}]:[];}next(){const e=this.dive();return this.backtrack(),e}dive(){if(this._path.length===0)return {done:true,value:undefined};const{node:e,keys:n}=z(this._path);if(z(n)===g)return {done:false,value:this.result()};const o=e.get(z(n));return this._path.push({node:o,keys:Array.from(o.keys())}),this.dive()}backtrack(){if(this._path.length===0)return;const e=z(this._path).keys;e.pop(),!(e.length>0)&&(this._path.pop(),this.backtrack());}key(){return this.set._prefix+this._path.map(({keys:e})=>z(e)).filter(e=>e!==g).join("")}value(){return z(this._path).node.get(g)}result(){switch(this._type){case G:return this.value();case B:return this.key();default:return [this.key(),this.value()]}}[Symbol.iterator](){return this}}const z=t=>t[t.length-1],zt=(t,e,n)=>{const o=new Map;if(typeof e!="string")return o;const s=e.length+1,r=s+n,i=new Uint8Array(r*s).fill(n+1);for(let c=0;c<s;++c)i[c]=c;for(let c=1;c<r;++c)i[c*s]=c;return K(t,e,n,o,i,1,s,""),o},K=(t,e,n,o,s,r,i,c)=>{const u=r*i;t:for(const d of t.keys())if(d===g){const a=s[u-1];a<=n&&o.set(c,[t.get(d),a]);}else {let a=r;for(let h=0;h<d.length;++h,++a){const f=d[h],_=i*a,p=_-i;let l=s[_];const m=Math.max(0,a-n-1),y=Math.min(i-1,a+n);for(let w=m;w<y;++w){const C=f!==e[w],O=s[p+w]+ +C,b=s[p+w+1]+1,x=s[_+w]+1,S=s[_+w+1]=Math.min(O,b,x);S<l&&(l=S);}if(l>n)continue t}K(t.get(d),e,n,o,s,a,i,c+d);}};class I{_tree;_prefix;_size=undefined;constructor(e=new Map,n=""){this._tree=e,this._prefix=n;}atPrefix(e){if(!e.startsWith(this._prefix))throw new Error("Mismatched prefix");const[n,o]=v(this._tree,e.slice(this._prefix.length));if(n===undefined){const[s,r]=L(o);for(const i of s.keys())if(i!==g&&i.startsWith(r)){const c=new Map;return c.set(i.slice(r.length),s.get(i)),new I(c,e)}}return new I(n,e)}clear(){this._size=undefined,this._tree.clear();}delete(e){return this._size=undefined,St(this._tree,e)}entries(){return new V(this,xt)}forEach(e){for(const[n,o]of this)e(n,o,this);}fuzzyGet(e,n){return zt(this._tree,e,n)}get(e){const n=T(this._tree,e);return n!==undefined?n.get(g):undefined}has(e){return T(this._tree,e)?.has(g)??false}keys(){return new V(this,B)}set(e,n){if(typeof e!="string")throw new Error("key must be a string");return this._size=undefined,M(this._tree,e).set(g,n),this}get size(){if(this._size)return this._size;this._size=0;const e=this.entries();for(;!e.next().done;)this._size+=1;return this._size}update(e,n){if(typeof e!="string")throw new Error("key must be a string");this._size=undefined;const o=M(this._tree,e);return o.set(g,n(o.get(g))),this}fetch(e,n){if(typeof e!="string")throw new Error("key must be a string");this._size=undefined;const o=M(this._tree,e);let s=o.get(g);return s===undefined&&o.set(g,s=n()),s}values(){return new V(this,G)}[Symbol.iterator](){return this.entries()}static from(e){const n=new I;for(const[o,s]of e)n.set(o,s);return n}static fromObject(e){return I.from(Object.entries(e))}}const v=(t,e,n=[])=>{if(e.length===0||t==null)return [t,n];for(const o of t.keys())if(o!==g&&e.startsWith(o))return n.push([t,o]),v(t.get(o),e.slice(o.length),n);return n.push([t,e]),v(undefined,"",n)},T=(t,e)=>{if(e.length===0||!t)return t;for(const n of t.keys())if(n!==g&&e.startsWith(n))return T(t.get(n),e.slice(n.length))},M=(t,e)=>{const n=e.length;t:for(let o=0;t&&o<n;){for(const r of t.keys())if(r!==g&&e[o]===r[0]){const i=Math.min(n-o,r.length);let c=1;for(;c<i&&e[o+c]===r[c];)++c;const u=t.get(r);if(c===r.length)t=u;else {const d=new Map;d.set(r.slice(c),u),t.set(e.slice(o,o+c),d),t.delete(r),t=d;}o+=c;continue t}const s=new Map;return t.set(e.slice(o),s),s}return t},St=(t,e)=>{const[n,o]=v(t,e);if(n!==undefined){if(n.delete(g),n.size===0)Q(o);else if(n.size===1){const[s,r]=n.entries().next().value;Y(o,s,r);}}},Q=t=>{if(t.length===0)return;const[e,n]=L(t);if(e.delete(n),e.size===0)Q(t.slice(0,-1));else if(e.size===1){const[o,s]=e.entries().next().value;o!==g&&Y(t.slice(0,-1),o,s);}},Y=(t,e,n)=>{if(t.length===0)return;const[o,s]=L(t);o.set(s+e,n),o.delete(s);},L=t=>t[t.length-1],Z=(t,e)=>t._idToShortId.has(e),vt=/[\n\r\p{Z}\p{P}]+/u,D="or",H="and",Ft="and_not",kt=(t,e)=>{t.includes(e)||t.push(e);},tt=(t,e)=>{for(const n of e)t.includes(n)||t.push(n);},et=({score:t},{score:e})=>e-t,nt=()=>new Map,E=(t,e)=>Object.prototype.hasOwnProperty.call(t,e)?t[e]:undefined,ot={[D]:(t,e)=>{for(const n of e.keys()){const o=t.get(n);if(o==null)t.set(n,e.get(n));else {const{score:s,terms:r,match:i}=e.get(n);o.score=o.score+s,o.match=Object.assign(o.match,i),tt(o.terms,r);}}return t},[H]:(t,e)=>{const n=new Map;for(const o of e.keys()){const s=t.get(o);if(s==null)continue;const{score:r,terms:i,match:c}=e.get(o);tt(s.terms,i),n.set(o,{score:s.score+r,terms:s.terms,match:Object.assign(s.match,c)});}return n},[Ft]:(t,e)=>{for(const n of e.keys())t.delete(n);return t}},Ct=(t,e,n,o,s,r)=>{const{k:i,b:c,d:u}=r;return Math.log(1+(n-e+.5)/(e+.5))*(u+t*(i+1)/(t+i*(1-c+c*o/s)))},Ot=t=>(e,n,o)=>({term:e,fuzzy:typeof t.fuzzy=="function"?t.fuzzy(e,n,o):t.fuzzy??false,prefix:typeof t.prefix=="function"?t.prefix(e,n,o):t.prefix===true,termBoost:typeof t.boostTerm=="function"?t.boostTerm(e,n,o):1}),st=(t,e,n,o)=>{for(const s of Object.keys(t._fieldIds))if(t._fieldIds[s]===n){t._options.logger("warn",`SlimSearch: document with ID ${t._documentIds.get(e)} has changed before removal: term "${o}" was not present in field "${s}". Removing a document after it has changed can corrupt the index!`,"version_conflict");return}},it=(t,e,n,o)=>{const s=t._index.fetch(o,nt);let r=s.get(e);if(r==null)r=new Map,r.set(n,1),s.set(e,r);else {const i=r.get(n);r.set(n,(i??0)+1);}},A=(t,e,n,o)=>{if(!t._index.has(o)){st(t,n,e,o);return}const s=t._index.fetch(o,nt),r=s.get(e),i=r?.get(n);!r||typeof i>"u"?st(t,n,e,o):i<=1?r.size<=1?s.delete(e):r.delete(n):r.set(n,i-1),t._index.get(o).size===0&&t._index.delete(o);},Vt=(t,e,n,o,s)=>{let r=t._fieldLength.get(e);r==null&&t._fieldLength.set(e,r=[]),r[n]=s;const i=(t._avgFieldLength[n]||0)*o+s;t._avgFieldLength[n]=i/(o+1);},Tt=(t,e)=>{const n=t._nextId;return t._idToShortId.set(e,n),t._documentIds.set(n,e),t._documentCount+=1,t._nextId+=1,n},Mt=(t,e,n)=>{const{storeFields:o,extractField:s}=t._options;if(o?.length===0)return;let r=t._storedFields.get(e);r===undefined&&t._storedFields.set(e,r={});for(const i of o){const c=s(n,i);c!=null&&(r[i]=c);}},j=(t,e)=>{const{extractField:n,tokenize:o,processTerm:s,fields:r,idField:i}=t._options,c=n(e,i);if(c==null)throw new Error(`SlimSearch: document does not have ID field "${i}"`);if(Z(t,c))throw new Error(`SlimSearch: duplicate ID ${c}`);const u=Tt(t,c);Mt(t,u,e);for(const d of r){const a=n(e,d);if(a==null)continue;const h=o(a.toString(),d),f=t._fieldIds[d],_=new Set(h).size;Vt(t,u,f,t._documentCount-1,_);for(const p of h){const l=s(p,d);if(Array.isArray(l))for(const m of l)it(t,f,u,m);else l&&it(t,f,u,l);}}},q=(t,e)=>{for(const n of e)j(t,n);},Dt={k:1.2,b:.7,d:.5},$={idField:"id",extractField:(t,e)=>t[e],tokenize:t=>t.split(vt),processTerm:t=>t.toLowerCase(),fields:undefined,searchOptions:undefined,storeFields:[],logger:(t,e)=>{console?.[t]?.(e);},autoVacuum:true},rt={combineWith:D,prefix:false,fuzzy:false,maxFuzzy:6,boost:{},weights:{fuzzy:.45,prefix:.375},bm25:Dt},Et={combineWith:H,prefix:(t,e,n)=>e===n.length-1},N={batchSize:1e3,batchWait:10},W={minDirtFactor:.1,minDirtCount:20},P={...N,...W},R=Symbol("*"),jt=(t,e)=>{const n=new Map,o={...t._options.searchOptions,...e};for(const[s,r]of t._documentIds){const i=o.boostDocument?o.boostDocument(r,"",t._storedFields.get(s)):1;n.set(s,{score:i,terms:[],match:{}});}return n},ct=(t,e=D)=>{if(t.length===0)return new Map;const n=e.toLowerCase();if(!(n in ot))throw new Error(`Invalid combination operator: ${e}`);return t.reduce(ot[n])},J=(t,e,n,o,s,r,i,c,u,d=new Map)=>{if(r==null)return d;for(const a of Object.keys(i)){const h=i[a],f=t._fieldIds[a],_=r.get(f);if(_==null)continue;let p=_.size;const l=t._avgFieldLength[f];for(const m of _.keys()){if(!t._documentIds.has(m)){A(t,f,m,n),p-=1;continue}const y=c?c(t._documentIds.get(m),n,t._storedFields.get(m)):1;if(!y)continue;const w=_.get(m),C=t._fieldLength.get(m)[f],O=Ct(w,p,t._documentCount,C,l,u),b=o*s*h*y*O,x=d.get(m);if(x){x.score+=b,kt(x.terms,e);const S=E(x.match,n);S?S.push(a):x.match[n]=[a];}else d.set(m,{score:b,terms:[e],match:{[n]:[a]}});}}return d},qt=(t,e,n)=>{const o={...t._options.searchOptions,...n},s=(o.fields??t._options.fields).reduce((l,m)=>({...l,[m]:E(o.boost,m)||1}),{}),{boostDocument:r,weights:i,maxFuzzy:c,bm25:u}=o,{fuzzy:d,prefix:a}={...rt.weights,...i},h=t._index.get(e.term),f=J(t,e.term,e.term,1,e.termBoost,h,s,r,u);let _,p;if(e.prefix&&(_=t._index.atPrefix(e.term)),e.fuzzy){const l=e.fuzzy===true?.2:e.fuzzy,m=l<1?Math.min(c,Math.round(e.term.length*l)):l;m&&(p=t._index.fuzzyGet(e.term,m));}if(_)for(const[l,m]of _){const y=l.length-e.term.length;if(!y)continue;p?.delete(l);const w=a*l.length/(l.length+.3*y);J(t,e.term,l,w,e.termBoost,m,s,r,u,f);}if(p)for(const l of p.keys()){const[m,y]=p.get(l);if(!y)continue;const w=d*l.length/(l.length+y);J(t,e.term,l,w,e.termBoost,m,s,r,u,f);}return f},ut=(t,e,n={})=>{if(e===R)return jt(t,n);if(typeof e!="string"){const a={...n,...e,queries:undefined},h=e.queries.map(f=>ut(t,f,a));return ct(h,a.combineWith)}const{tokenize:o,processTerm:s,searchOptions:r}=t._options,i={tokenize:o,processTerm:s,...r,...n},{tokenize:c,processTerm:u}=i,d=c(e).flatMap(a=>u(a)).filter(a=>!!a).map(Ot(i)).map(a=>qt(t,a,i));return ct(d,i.combineWith)},dt=(t,e,n={})=>{const{searchOptions:o}=t._options,s={...o,...n},r=ut(t,e,n),i=[];for(const[c,{score:u,terms:d,match:a}]of r){const h=d.length||1,f={id:t._documentIds.get(c),score:u*h,terms:Object.keys(a),queryTerms:d,match:a};Object.assign(f,t._storedFields.get(c)),(s.filter==null||s.filter(f))&&i.push(f);}return e===R&&s.boostDocument==null||i.sort(et),i};class Nt{_options;_index;_documentCount;_documentIds;_idToShortId;_fieldIds;_fieldLength;_avgFieldLength;_nextId;_storedFields;_dirtCount;_currentVacuum;_enqueuedVacuum;_enqueuedVacuumConditions;constructor(e){if(!e?.fields)throw new Error('SlimSearch: option "fields" must be provided');const n=e.autoVacuum==null||e.autoVacuum===true?P:e.autoVacuum;this._options={...$,...e,autoVacuum:n,searchOptions:{...rt,...e.searchOptions},autoSuggestOptions:{...Et,...e.autoSuggestOptions}},this._index=new I,this._documentCount=0,this._documentIds=new Map,this._idToShortId=new Map,this._fieldIds={},this._fieldLength=new Map,this._avgFieldLength=[],this._nextId=0,this._storedFields=new Map,this._dirtCount=0,this._currentVacuum=null,this._enqueuedVacuum=null,this._enqueuedVacuumConditions=W,this.addFields(this._options.fields);}get isVacuuming(){return this._currentVacuum!=null}get dirtCount(){return this._dirtCount}get dirtFactor(){return this._dirtCount/(1+this._documentCount+this._dirtCount)}get documentCount(){return this._documentCount}get termCount(){return this._index.size}toJSON(){const e=[];for(const[n,o]of this._index){const s={};for(const[r,i]of o)s[r]=Object.fromEntries(i);e.push([n,s]);}return {documentCount:this._documentCount,nextId:this._nextId,documentIds:Object.fromEntries(this._documentIds),fieldIds:this._fieldIds,fieldLength:Object.fromEntries(this._fieldLength),averageFieldLength:this._avgFieldLength,storedFields:Object.fromEntries(this._storedFields),dirtCount:this._dirtCount,index:e,version:2}}addFields(e){for(let n=0;n<e.length;n++)this._fieldIds[e[n]]=n;}}const lt=t=>new Nt(t);
11334
+ const xt="ENTRIES",B="KEYS",G="VALUES",g="";class V{set;_type;_path;constructor(e,n){const o=e._tree,s=Array.from(o.keys());this.set=e,this._type=n,this._path=s.length>0?[{node:o,keys:s}]:[];}next(){const e=this.dive();return this.backtrack(),e}dive(){if(this._path.length===0)return {done:true,value:void 0};const{node:e,keys:n}=z(this._path);if(z(n)===g)return {done:false,value:this.result()};const o=e.get(z(n));return this._path.push({node:o,keys:Array.from(o.keys())}),this.dive()}backtrack(){if(this._path.length===0)return;const e=z(this._path).keys;e.pop(),!(e.length>0)&&(this._path.pop(),this.backtrack());}key(){return this.set._prefix+this._path.map(({keys:e})=>z(e)).filter(e=>e!==g).join("")}value(){return z(this._path).node.get(g)}result(){switch(this._type){case G:return this.value();case B:return this.key();default:return [this.key(),this.value()]}}[Symbol.iterator](){return this}}const z=t=>t[t.length-1],zt=(t,e,n)=>{const o=new Map;if(typeof e!="string")return o;const s=e.length+1,r=s+n,i=new Uint8Array(r*s).fill(n+1);for(let c=0;c<s;++c)i[c]=c;for(let c=1;c<r;++c)i[c*s]=c;return K(t,e,n,o,i,1,s,""),o},K=(t,e,n,o,s,r,i,c)=>{const u=r*i;t:for(const d of t.keys())if(d===g){const a=s[u-1];a<=n&&o.set(c,[t.get(d),a]);}else {let a=r;for(let h=0;h<d.length;++h,++a){const f=d[h],_=i*a,p=_-i;let l=s[_];const m=Math.max(0,a-n-1),y=Math.min(i-1,a+n);for(let w=m;w<y;++w){const C=f!==e[w],O=s[p+w]+ +C,b=s[p+w+1]+1,x=s[_+w]+1,S=s[_+w+1]=Math.min(O,b,x);S<l&&(l=S);}if(l>n)continue t}K(t.get(d),e,n,o,s,a,i,c+d);}};class I{_tree;_prefix;_size=void 0;constructor(e=new Map,n=""){this._tree=e,this._prefix=n;}atPrefix(e){if(!e.startsWith(this._prefix))throw new Error("Mismatched prefix");const[n,o]=v(this._tree,e.slice(this._prefix.length));if(n===void 0){const[s,r]=L(o);for(const i of s.keys())if(i!==g&&i.startsWith(r)){const c=new Map;return c.set(i.slice(r.length),s.get(i)),new I(c,e)}}return new I(n,e)}clear(){this._size=void 0,this._tree.clear();}delete(e){return this._size=void 0,St(this._tree,e)}entries(){return new V(this,xt)}forEach(e){for(const[n,o]of this)e(n,o,this);}fuzzyGet(e,n){return zt(this._tree,e,n)}get(e){const n=T(this._tree,e);return n!==void 0?n.get(g):void 0}has(e){return T(this._tree,e)?.has(g)??false}keys(){return new V(this,B)}set(e,n){if(typeof e!="string")throw new Error("key must be a string");return this._size=void 0,M(this._tree,e).set(g,n),this}get size(){if(this._size)return this._size;this._size=0;const e=this.entries();for(;!e.next().done;)this._size+=1;return this._size}update(e,n){if(typeof e!="string")throw new Error("key must be a string");this._size=void 0;const o=M(this._tree,e);return o.set(g,n(o.get(g))),this}fetch(e,n){if(typeof e!="string")throw new Error("key must be a string");this._size=void 0;const o=M(this._tree,e);let s=o.get(g);return s===void 0&&o.set(g,s=n()),s}values(){return new V(this,G)}[Symbol.iterator](){return this.entries()}static from(e){const n=new I;for(const[o,s]of e)n.set(o,s);return n}static fromObject(e){return I.from(Object.entries(e))}}const v=(t,e,n=[])=>{if(e.length===0||t==null)return [t,n];for(const o of t.keys())if(o!==g&&e.startsWith(o))return n.push([t,o]),v(t.get(o),e.slice(o.length),n);return n.push([t,e]),v(void 0,"",n)},T=(t,e)=>{if(e.length===0||!t)return t;for(const n of t.keys())if(n!==g&&e.startsWith(n))return T(t.get(n),e.slice(n.length))},M=(t,e)=>{const n=e.length;t:for(let o=0;t&&o<n;){for(const r of t.keys())if(r!==g&&e[o]===r[0]){const i=Math.min(n-o,r.length);let c=1;for(;c<i&&e[o+c]===r[c];)++c;const u=t.get(r);if(c===r.length)t=u;else {const d=new Map;d.set(r.slice(c),u),t.set(e.slice(o,o+c),d),t.delete(r),t=d;}o+=c;continue t}const s=new Map;return t.set(e.slice(o),s),s}return t},St=(t,e)=>{const[n,o]=v(t,e);if(n!==void 0){if(n.delete(g),n.size===0)Q(o);else if(n.size===1){const[s,r]=n.entries().next().value;Y(o,s,r);}}},Q=t=>{if(t.length===0)return;const[e,n]=L(t);if(e.delete(n),e.size===0)Q(t.slice(0,-1));else if(e.size===1){const[o,s]=e.entries().next().value;o!==g&&Y(t.slice(0,-1),o,s);}},Y=(t,e,n)=>{if(t.length===0)return;const[o,s]=L(t);o.set(s+e,n),o.delete(s);},L=t=>t[t.length-1],Z=(t,e)=>t._idToShortId.has(e),vt=/[\n\r\p{Z}\p{P}]+/u,D="or",H="and",Ft="and_not",kt=(t,e)=>{t.includes(e)||t.push(e);},tt=(t,e)=>{for(const n of e)t.includes(n)||t.push(n);},et=({score:t},{score:e})=>e-t,nt=()=>new Map,E=(t,e)=>Object.prototype.hasOwnProperty.call(t,e)?t[e]:void 0,ot={[D]:(t,e)=>{for(const n of e.keys()){const o=t.get(n);if(o==null)t.set(n,e.get(n));else {const{score:s,terms:r,match:i}=e.get(n);o.score=o.score+s,o.match=Object.assign(o.match,i),tt(o.terms,r);}}return t},[H]:(t,e)=>{const n=new Map;for(const o of e.keys()){const s=t.get(o);if(s==null)continue;const{score:r,terms:i,match:c}=e.get(o);tt(s.terms,i),n.set(o,{score:s.score+r,terms:s.terms,match:Object.assign(s.match,c)});}return n},[Ft]:(t,e)=>{for(const n of e.keys())t.delete(n);return t}},Ct=(t,e,n,o,s,r)=>{const{k:i,b:c,d:u}=r;return Math.log(1+(n-e+.5)/(e+.5))*(u+t*(i+1)/(t+i*(1-c+c*o/s)))},Ot=t=>(e,n,o)=>({term:e,fuzzy:typeof t.fuzzy=="function"?t.fuzzy(e,n,o):t.fuzzy??false,prefix:typeof t.prefix=="function"?t.prefix(e,n,o):t.prefix===true,termBoost:typeof t.boostTerm=="function"?t.boostTerm(e,n,o):1}),st=(t,e,n,o)=>{for(const s of Object.keys(t._fieldIds))if(t._fieldIds[s]===n){t._options.logger("warn",`SlimSearch: document with ID ${t._documentIds.get(e)} has changed before removal: term "${o}" was not present in field "${s}". Removing a document after it has changed can corrupt the index!`,"version_conflict");return}},it=(t,e,n,o)=>{const s=t._index.fetch(o,nt);let r=s.get(e);if(r==null)r=new Map,r.set(n,1),s.set(e,r);else {const i=r.get(n);r.set(n,(i??0)+1);}},A=(t,e,n,o)=>{if(!t._index.has(o)){st(t,n,e,o);return}const s=t._index.fetch(o,nt),r=s.get(e),i=r?.get(n);!r||typeof i>"u"?st(t,n,e,o):i<=1?r.size<=1?s.delete(e):r.delete(n):r.set(n,i-1),t._index.get(o).size===0&&t._index.delete(o);},Vt=(t,e,n,o,s)=>{let r=t._fieldLength.get(e);r==null&&t._fieldLength.set(e,r=[]),r[n]=s;const i=(t._avgFieldLength[n]||0)*o+s;t._avgFieldLength[n]=i/(o+1);},Tt=(t,e)=>{const n=t._nextId;return t._idToShortId.set(e,n),t._documentIds.set(n,e),t._documentCount+=1,t._nextId+=1,n},Mt=(t,e,n)=>{const{storeFields:o,extractField:s}=t._options;if(o?.length===0)return;let r=t._storedFields.get(e);r===void 0&&t._storedFields.set(e,r={});for(const i of o){const c=s(n,i);c!=null&&(r[i]=c);}},j=(t,e)=>{const{extractField:n,tokenize:o,processTerm:s,fields:r,idField:i}=t._options,c=n(e,i);if(c==null)throw new Error(`SlimSearch: document does not have ID field "${i}"`);if(Z(t,c))throw new Error(`SlimSearch: duplicate ID ${c}`);const u=Tt(t,c);Mt(t,u,e);for(const d of r){const a=n(e,d);if(a==null)continue;const h=o(a.toString(),d),f=t._fieldIds[d],_=new Set(h).size;Vt(t,u,f,t._documentCount-1,_);for(const p of h){const l=s(p,d);if(Array.isArray(l))for(const m of l)it(t,f,u,m);else l&&it(t,f,u,l);}}},q=(t,e)=>{for(const n of e)j(t,n);},Dt={k:1.2,b:.7,d:.5},$={idField:"id",extractField:(t,e)=>t[e],tokenize:t=>t.split(vt),processTerm:t=>t.toLowerCase(),fields:void 0,searchOptions:void 0,storeFields:[],logger:(t,e)=>{console?.[t]?.(e);},autoVacuum:true},rt={combineWith:D,prefix:false,fuzzy:false,maxFuzzy:6,boost:{},weights:{fuzzy:.45,prefix:.375},bm25:Dt},Et={combineWith:H,prefix:(t,e,n)=>e===n.length-1},N={batchSize:1e3,batchWait:10},W={minDirtFactor:.1,minDirtCount:20},P={...N,...W},R=Symbol("*"),jt=(t,e)=>{const n=new Map,o={...t._options.searchOptions,...e};for(const[s,r]of t._documentIds){const i=o.boostDocument?o.boostDocument(r,"",t._storedFields.get(s)):1;n.set(s,{score:i,terms:[],match:{}});}return n},ct=(t,e=D)=>{if(t.length===0)return new Map;const n=e.toLowerCase();if(!(n in ot))throw new Error(`Invalid combination operator: ${e}`);return t.reduce(ot[n])},J=(t,e,n,o,s,r,i,c,u,d=new Map)=>{if(r==null)return d;for(const a of Object.keys(i)){const h=i[a],f=t._fieldIds[a],_=r.get(f);if(_==null)continue;let p=_.size;const l=t._avgFieldLength[f];for(const m of _.keys()){if(!t._documentIds.has(m)){A(t,f,m,n),p-=1;continue}const y=c?c(t._documentIds.get(m),n,t._storedFields.get(m)):1;if(!y)continue;const w=_.get(m),C=t._fieldLength.get(m)[f],O=Ct(w,p,t._documentCount,C,l,u),b=o*s*h*y*O,x=d.get(m);if(x){x.score+=b,kt(x.terms,e);const S=E(x.match,n);S?S.push(a):x.match[n]=[a];}else d.set(m,{score:b,terms:[e],match:{[n]:[a]}});}}return d},qt=(t,e,n)=>{const o={...t._options.searchOptions,...n},s=(o.fields??t._options.fields).reduce((l,m)=>({...l,[m]:E(o.boost,m)||1}),{}),{boostDocument:r,weights:i,maxFuzzy:c,bm25:u}=o,{fuzzy:d,prefix:a}={...rt.weights,...i},h=t._index.get(e.term),f=J(t,e.term,e.term,1,e.termBoost,h,s,r,u);let _,p;if(e.prefix&&(_=t._index.atPrefix(e.term)),e.fuzzy){const l=e.fuzzy===true?.2:e.fuzzy,m=l<1?Math.min(c,Math.round(e.term.length*l)):l;m&&(p=t._index.fuzzyGet(e.term,m));}if(_)for(const[l,m]of _){const y=l.length-e.term.length;if(!y)continue;p?.delete(l);const w=a*l.length/(l.length+.3*y);J(t,e.term,l,w,e.termBoost,m,s,r,u,f);}if(p)for(const l of p.keys()){const[m,y]=p.get(l);if(!y)continue;const w=d*l.length/(l.length+y);J(t,e.term,l,w,e.termBoost,m,s,r,u,f);}return f},ut=(t,e,n={})=>{if(e===R)return jt(t,n);if(typeof e!="string"){const a={...n,...e,queries:void 0},h=e.queries.map(f=>ut(t,f,a));return ct(h,a.combineWith)}const{tokenize:o,processTerm:s,searchOptions:r}=t._options,i={tokenize:o,processTerm:s,...r,...n},{tokenize:c,processTerm:u}=i,d=c(e).flatMap(a=>u(a)).filter(a=>!!a).map(Ot(i)).map(a=>qt(t,a,i));return ct(d,i.combineWith)},dt=(t,e,n={})=>{const{searchOptions:o}=t._options,s={...o,...n},r=ut(t,e,n),i=[];for(const[c,{score:u,terms:d,match:a}]of r){const h=d.length||1,f={id:t._documentIds.get(c),score:u*h,terms:Object.keys(a),queryTerms:d,match:a};Object.assign(f,t._storedFields.get(c)),(s.filter==null||s.filter(f))&&i.push(f);}return e===R&&s.boostDocument==null||i.sort(et),i};class Nt{_options;_index;_documentCount;_documentIds;_idToShortId;_fieldIds;_fieldLength;_avgFieldLength;_nextId;_storedFields;_dirtCount;_currentVacuum;_enqueuedVacuum;_enqueuedVacuumConditions;constructor(e){if(!e?.fields)throw new Error('SlimSearch: option "fields" must be provided');const n=e.autoVacuum==null||e.autoVacuum===true?P:e.autoVacuum;this._options={...$,...e,autoVacuum:n,searchOptions:{...rt,...e.searchOptions},autoSuggestOptions:{...Et,...e.autoSuggestOptions}},this._index=new I,this._documentCount=0,this._documentIds=new Map,this._idToShortId=new Map,this._fieldIds={},this._fieldLength=new Map,this._avgFieldLength=[],this._nextId=0,this._storedFields=new Map,this._dirtCount=0,this._currentVacuum=null,this._enqueuedVacuum=null,this._enqueuedVacuumConditions=W,this.addFields(this._options.fields);}get isVacuuming(){return this._currentVacuum!=null}get dirtCount(){return this._dirtCount}get dirtFactor(){return this._dirtCount/(1+this._documentCount+this._dirtCount)}get documentCount(){return this._documentCount}get termCount(){return this._index.size}toJSON(){const e=[];for(const[n,o]of this._index){const s={};for(const[r,i]of o)s[r]=Object.fromEntries(i);e.push([n,s]);}return {documentCount:this._documentCount,nextId:this._nextId,documentIds:Object.fromEntries(this._documentIds),fieldIds:this._fieldIds,fieldLength:Object.fromEntries(this._fieldLength),averageFieldLength:this._avgFieldLength,storedFields:Object.fromEntries(this._storedFields),dirtCount:this._dirtCount,index:e,version:2}}addFields(e){for(let n=0;n<e.length;n++)this._fieldIds[e[n]]=n;}}const lt=t=>new Nt(t);
11188
11335
 
11189
11336
  /**
11190
11337
  * Generates a list of accent variants for a given term.
@@ -11226,7 +11373,7 @@ const generateAccentVariants = (term) => {
11226
11373
 
11227
11374
  // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
11228
11375
  // versions:
11229
- // protoc-gen-ts_proto v2.6.1
11376
+ // protoc-gen-ts_proto v2.7.7
11230
11377
  // protoc v4.23.4
11231
11378
  // source: src/stops/proto/stops.proto
11232
11379
  /* eslint-disable */
@@ -11323,7 +11470,7 @@ const Stop = {
11323
11470
  },
11324
11471
  decode(input, length) {
11325
11472
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
11326
- let end = length === undefined ? reader.len : reader.pos + length;
11473
+ const end = length === undefined ? reader.len : reader.pos + length;
11327
11474
  const message = createBaseStop();
11328
11475
  while (reader.pos < end) {
11329
11476
  const tag = reader.uint32();
@@ -11405,7 +11552,7 @@ const Stop = {
11405
11552
  sourceStopId: isSet$1(object.sourceStopId) ? globalThis.String(object.sourceStopId) : "",
11406
11553
  lat: isSet$1(object.lat) ? globalThis.Number(object.lat) : undefined,
11407
11554
  lon: isSet$1(object.lon) ? globalThis.Number(object.lon) : undefined,
11408
- children: globalThis.Array.isArray(object === null || object === undefined ? undefined : object.children) ? object.children.map((e) => globalThis.Number(e)) : [],
11555
+ children: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.children) ? object.children.map((e) => globalThis.Number(e)) : [],
11409
11556
  parent: isSet$1(object.parent) ? globalThis.Number(object.parent) : undefined,
11410
11557
  locationType: isSet$1(object.locationType) ? locationTypeFromJSON(object.locationType) : 0,
11411
11558
  platform: isSet$1(object.platform) ? globalThis.String(object.platform) : undefined,
@@ -11426,7 +11573,7 @@ const Stop = {
11426
11573
  if (message.lon !== undefined) {
11427
11574
  obj.lon = message.lon;
11428
11575
  }
11429
- if ((_a = message.children) === null || _a === undefined ? undefined : _a.length) {
11576
+ if ((_a = message.children) === null || _a === void 0 ? void 0 : _a.length) {
11430
11577
  obj.children = message.children.map((e) => Math.round(e));
11431
11578
  }
11432
11579
  if (message.parent !== undefined) {
@@ -11441,19 +11588,19 @@ const Stop = {
11441
11588
  return obj;
11442
11589
  },
11443
11590
  create(base) {
11444
- return Stop.fromPartial(base !== null && base !== undefined ? base : {});
11591
+ return Stop.fromPartial(base !== null && base !== void 0 ? base : {});
11445
11592
  },
11446
11593
  fromPartial(object) {
11447
11594
  var _a, _b, _c, _d, _e, _f, _g, _h;
11448
11595
  const message = createBaseStop();
11449
- message.name = (_a = object.name) !== null && _a !== undefined ? _a : "";
11450
- message.sourceStopId = (_b = object.sourceStopId) !== null && _b !== undefined ? _b : "";
11451
- message.lat = (_c = object.lat) !== null && _c !== undefined ? _c : undefined;
11452
- message.lon = (_d = object.lon) !== null && _d !== undefined ? _d : undefined;
11453
- message.children = ((_e = object.children) === null || _e === undefined ? undefined : _e.map((e) => e)) || [];
11454
- message.parent = (_f = object.parent) !== null && _f !== undefined ? _f : undefined;
11455
- message.locationType = (_g = object.locationType) !== null && _g !== undefined ? _g : 0;
11456
- message.platform = (_h = object.platform) !== null && _h !== undefined ? _h : undefined;
11596
+ message.name = (_a = object.name) !== null && _a !== void 0 ? _a : "";
11597
+ message.sourceStopId = (_b = object.sourceStopId) !== null && _b !== void 0 ? _b : "";
11598
+ message.lat = (_c = object.lat) !== null && _c !== void 0 ? _c : undefined;
11599
+ message.lon = (_d = object.lon) !== null && _d !== void 0 ? _d : undefined;
11600
+ message.children = ((_e = object.children) === null || _e === void 0 ? void 0 : _e.map((e) => e)) || [];
11601
+ message.parent = (_f = object.parent) !== null && _f !== void 0 ? _f : undefined;
11602
+ message.locationType = (_g = object.locationType) !== null && _g !== void 0 ? _g : 0;
11603
+ message.platform = (_h = object.platform) !== null && _h !== void 0 ? _h : undefined;
11457
11604
  return message;
11458
11605
  },
11459
11606
  };
@@ -11472,7 +11619,7 @@ const StopsMap = {
11472
11619
  },
11473
11620
  decode(input, length) {
11474
11621
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
11475
- let end = length === undefined ? reader.len : reader.pos + length;
11622
+ const end = length === undefined ? reader.len : reader.pos + length;
11476
11623
  const message = createBaseStopsMap();
11477
11624
  while (reader.pos < end) {
11478
11625
  const tag = reader.uint32();
@@ -11530,13 +11677,13 @@ const StopsMap = {
11530
11677
  return obj;
11531
11678
  },
11532
11679
  create(base) {
11533
- return StopsMap.fromPartial(base !== null && base !== undefined ? base : {});
11680
+ return StopsMap.fromPartial(base !== null && base !== void 0 ? base : {});
11534
11681
  },
11535
11682
  fromPartial(object) {
11536
11683
  var _a, _b;
11537
11684
  const message = createBaseStopsMap();
11538
- message.version = (_a = object.version) !== null && _a !== undefined ? _a : "";
11539
- message.stops = Object.entries((_b = object.stops) !== null && _b !== undefined ? _b : {}).reduce((acc, [key, value]) => {
11685
+ message.version = (_a = object.version) !== null && _a !== void 0 ? _a : "";
11686
+ message.stops = Object.entries((_b = object.stops) !== null && _b !== void 0 ? _b : {}).reduce((acc, [key, value]) => {
11540
11687
  if (value !== undefined) {
11541
11688
  acc[globalThis.Number(key)] = Stop.fromPartial(value);
11542
11689
  }
@@ -11560,7 +11707,7 @@ const StopsMap_StopsEntry = {
11560
11707
  },
11561
11708
  decode(input, length) {
11562
11709
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
11563
- let end = length === undefined ? reader.len : reader.pos + length;
11710
+ const end = length === undefined ? reader.len : reader.pos + length;
11564
11711
  const message = createBaseStopsMap_StopsEntry();
11565
11712
  while (reader.pos < end) {
11566
11713
  const tag = reader.uint32();
@@ -11604,12 +11751,12 @@ const StopsMap_StopsEntry = {
11604
11751
  return obj;
11605
11752
  },
11606
11753
  create(base) {
11607
- return StopsMap_StopsEntry.fromPartial(base !== null && base !== undefined ? base : {});
11754
+ return StopsMap_StopsEntry.fromPartial(base !== null && base !== void 0 ? base : {});
11608
11755
  },
11609
11756
  fromPartial(object) {
11610
11757
  var _a;
11611
11758
  const message = createBaseStopsMap_StopsEntry();
11612
- message.key = (_a = object.key) !== null && _a !== undefined ? _a : 0;
11759
+ message.key = (_a = object.key) !== null && _a !== void 0 ? _a : 0;
11613
11760
  message.value = (object.value !== undefined && object.value !== null) ? Stop.fromPartial(object.value) : undefined;
11614
11761
  return message;
11615
11762
  },
@@ -11720,7 +11867,7 @@ class StopsIndex {
11720
11867
  });
11721
11868
  const stopsSet = new Map();
11722
11869
  for (const [id, stop] of stopsMap.entries()) {
11723
- const effectiveStopId = (_a = stop.parent) !== null && _a !== undefined ? _a : id;
11870
+ const effectiveStopId = (_a = stop.parent) !== null && _a !== void 0 ? _a : id;
11724
11871
  if (!stopsSet.has(effectiveStopId)) {
11725
11872
  stopsSet.set(effectiveStopId, {
11726
11873
  id: effectiveStopId,
@@ -11751,8 +11898,8 @@ class StopsIndex {
11751
11898
  /**
11752
11899
  * Deserializes a binary representation of the stops.
11753
11900
  *
11754
- * @param {Uint8Array} data - The binary data to deserialize.
11755
- * @returns {StopsMap} - The deserialized StopFinder.
11901
+ * @param data - The binary data to deserialize.
11902
+ * @returns The deserialized StopFinder.
11756
11903
  */
11757
11904
  static fromData(data) {
11758
11905
  const reader = new BinaryReader(data);
@@ -11762,7 +11909,7 @@ class StopsIndex {
11762
11909
  /**
11763
11910
  * Serializes the stops into a binary protobuf.
11764
11911
  *
11765
- * @returns {Uint8Array} - The serialized binary data.
11912
+ * @returns The serialized binary data.
11766
11913
  */
11767
11914
  serialize() {
11768
11915
  const protoStopsMap = serializeStopsMap(this.stopsMap);
@@ -11841,7 +11988,7 @@ class StopsIndex {
11841
11988
  return [];
11842
11989
  }
11843
11990
  const equivalentStops = stop.parent
11844
- ? ((_b = (_a = this.stopsMap.get(stop.parent)) === null || _a === undefined ? undefined : _a.children) !== null && _b !== undefined ? _b : [])
11991
+ ? ((_b = (_a = this.stopsMap.get(stop.parent)) === null || _a === void 0 ? void 0 : _a.children) !== null && _b !== void 0 ? _b : [])
11845
11992
  : stop.children;
11846
11993
  return Array.from(new Set([id, ...equivalentStops])).map((stopId) => this.stopsMap.get(stopId));
11847
11994
  }
@@ -11930,7 +12077,7 @@ class Duration {
11930
12077
 
11931
12078
  // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
11932
12079
  // versions:
11933
- // protoc-gen-ts_proto v2.6.1
12080
+ // protoc-gen-ts_proto v2.7.7
11934
12081
  // protoc v4.23.4
11935
12082
  // source: src/timetable/proto/timetable.proto
11936
12083
  /* eslint-disable */
@@ -12064,7 +12211,7 @@ function createBaseRoute() {
12064
12211
  serviceRouteId: "",
12065
12212
  };
12066
12213
  }
12067
- const Route = {
12214
+ const Route$1 = {
12068
12215
  encode(message, writer = new BinaryWriter()) {
12069
12216
  if (message.stopTimes.length !== 0) {
12070
12217
  writer.uint32(10).bytes(message.stopTimes);
@@ -12082,7 +12229,7 @@ const Route = {
12082
12229
  },
12083
12230
  decode(input, length) {
12084
12231
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12085
- let end = length === undefined ? reader.len : reader.pos + length;
12232
+ const end = length === undefined ? reader.len : reader.pos + length;
12086
12233
  const message = createBaseRoute();
12087
12234
  while (reader.pos < end) {
12088
12235
  const tag = reader.uint32();
@@ -12150,15 +12297,15 @@ const Route = {
12150
12297
  return obj;
12151
12298
  },
12152
12299
  create(base) {
12153
- return Route.fromPartial(base !== null && base !== undefined ? base : {});
12300
+ return Route$1.fromPartial(base !== null && base !== void 0 ? base : {});
12154
12301
  },
12155
12302
  fromPartial(object) {
12156
12303
  var _a, _b, _c, _d;
12157
12304
  const message = createBaseRoute();
12158
- message.stopTimes = (_a = object.stopTimes) !== null && _a !== undefined ? _a : new Uint8Array(0);
12159
- message.pickUpDropOffTypes = (_b = object.pickUpDropOffTypes) !== null && _b !== undefined ? _b : new Uint8Array(0);
12160
- message.stops = (_c = object.stops) !== null && _c !== undefined ? _c : new Uint8Array(0);
12161
- message.serviceRouteId = (_d = object.serviceRouteId) !== null && _d !== undefined ? _d : "";
12305
+ message.stopTimes = (_a = object.stopTimes) !== null && _a !== void 0 ? _a : new Uint8Array(0);
12306
+ message.pickUpDropOffTypes = (_b = object.pickUpDropOffTypes) !== null && _b !== void 0 ? _b : new Uint8Array(0);
12307
+ message.stops = (_c = object.stops) !== null && _c !== void 0 ? _c : new Uint8Array(0);
12308
+ message.serviceRouteId = (_d = object.serviceRouteId) !== null && _d !== void 0 ? _d : "";
12162
12309
  return message;
12163
12310
  },
12164
12311
  };
@@ -12174,7 +12321,7 @@ const RoutesAdjacency = {
12174
12321
  },
12175
12322
  decode(input, length) {
12176
12323
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12177
- let end = length === undefined ? reader.len : reader.pos + length;
12324
+ const end = length === undefined ? reader.len : reader.pos + length;
12178
12325
  const message = createBaseRoutesAdjacency();
12179
12326
  while (reader.pos < end) {
12180
12327
  const tag = reader.uint32();
@@ -12201,7 +12348,7 @@ const RoutesAdjacency = {
12201
12348
  return {
12202
12349
  routes: isObject(object.routes)
12203
12350
  ? Object.entries(object.routes).reduce((acc, [key, value]) => {
12204
- acc[key] = Route.fromJSON(value);
12351
+ acc[key] = Route$1.fromJSON(value);
12205
12352
  return acc;
12206
12353
  }, {})
12207
12354
  : {},
@@ -12214,21 +12361,21 @@ const RoutesAdjacency = {
12214
12361
  if (entries.length > 0) {
12215
12362
  obj.routes = {};
12216
12363
  entries.forEach(([k, v]) => {
12217
- obj.routes[k] = Route.toJSON(v);
12364
+ obj.routes[k] = Route$1.toJSON(v);
12218
12365
  });
12219
12366
  }
12220
12367
  }
12221
12368
  return obj;
12222
12369
  },
12223
12370
  create(base) {
12224
- return RoutesAdjacency.fromPartial(base !== null && base !== undefined ? base : {});
12371
+ return RoutesAdjacency.fromPartial(base !== null && base !== void 0 ? base : {});
12225
12372
  },
12226
12373
  fromPartial(object) {
12227
12374
  var _a;
12228
12375
  const message = createBaseRoutesAdjacency();
12229
- message.routes = Object.entries((_a = object.routes) !== null && _a !== undefined ? _a : {}).reduce((acc, [key, value]) => {
12376
+ message.routes = Object.entries((_a = object.routes) !== null && _a !== void 0 ? _a : {}).reduce((acc, [key, value]) => {
12230
12377
  if (value !== undefined) {
12231
- acc[key] = Route.fromPartial(value);
12378
+ acc[key] = Route$1.fromPartial(value);
12232
12379
  }
12233
12380
  return acc;
12234
12381
  }, {});
@@ -12244,13 +12391,13 @@ const RoutesAdjacency_RoutesEntry = {
12244
12391
  writer.uint32(10).string(message.key);
12245
12392
  }
12246
12393
  if (message.value !== undefined) {
12247
- Route.encode(message.value, writer.uint32(18).fork()).join();
12394
+ Route$1.encode(message.value, writer.uint32(18).fork()).join();
12248
12395
  }
12249
12396
  return writer;
12250
12397
  },
12251
12398
  decode(input, length) {
12252
12399
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12253
- let end = length === undefined ? reader.len : reader.pos + length;
12400
+ const end = length === undefined ? reader.len : reader.pos + length;
12254
12401
  const message = createBaseRoutesAdjacency_RoutesEntry();
12255
12402
  while (reader.pos < end) {
12256
12403
  const tag = reader.uint32();
@@ -12266,7 +12413,7 @@ const RoutesAdjacency_RoutesEntry = {
12266
12413
  if (tag !== 18) {
12267
12414
  break;
12268
12415
  }
12269
- message.value = Route.decode(reader, reader.uint32());
12416
+ message.value = Route$1.decode(reader, reader.uint32());
12270
12417
  continue;
12271
12418
  }
12272
12419
  }
@@ -12280,7 +12427,7 @@ const RoutesAdjacency_RoutesEntry = {
12280
12427
  fromJSON(object) {
12281
12428
  return {
12282
12429
  key: isSet(object.key) ? globalThis.String(object.key) : "",
12283
- value: isSet(object.value) ? Route.fromJSON(object.value) : undefined,
12430
+ value: isSet(object.value) ? Route$1.fromJSON(object.value) : undefined,
12284
12431
  };
12285
12432
  },
12286
12433
  toJSON(message) {
@@ -12289,18 +12436,18 @@ const RoutesAdjacency_RoutesEntry = {
12289
12436
  obj.key = message.key;
12290
12437
  }
12291
12438
  if (message.value !== undefined) {
12292
- obj.value = Route.toJSON(message.value);
12439
+ obj.value = Route$1.toJSON(message.value);
12293
12440
  }
12294
12441
  return obj;
12295
12442
  },
12296
12443
  create(base) {
12297
- return RoutesAdjacency_RoutesEntry.fromPartial(base !== null && base !== undefined ? base : {});
12444
+ return RoutesAdjacency_RoutesEntry.fromPartial(base !== null && base !== void 0 ? base : {});
12298
12445
  },
12299
12446
  fromPartial(object) {
12300
12447
  var _a;
12301
12448
  const message = createBaseRoutesAdjacency_RoutesEntry();
12302
- message.key = (_a = object.key) !== null && _a !== undefined ? _a : "";
12303
- message.value = (object.value !== undefined && object.value !== null) ? Route.fromPartial(object.value) : undefined;
12449
+ message.key = (_a = object.key) !== null && _a !== void 0 ? _a : "";
12450
+ message.value = (object.value !== undefined && object.value !== null) ? Route$1.fromPartial(object.value) : undefined;
12304
12451
  return message;
12305
12452
  },
12306
12453
  };
@@ -12322,7 +12469,7 @@ const Transfer = {
12322
12469
  },
12323
12470
  decode(input, length) {
12324
12471
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12325
- let end = length === undefined ? reader.len : reader.pos + length;
12472
+ const end = length === undefined ? reader.len : reader.pos + length;
12326
12473
  const message = createBaseTransfer();
12327
12474
  while (reader.pos < end) {
12328
12475
  const tag = reader.uint32();
@@ -12377,14 +12524,14 @@ const Transfer = {
12377
12524
  return obj;
12378
12525
  },
12379
12526
  create(base) {
12380
- return Transfer.fromPartial(base !== null && base !== undefined ? base : {});
12527
+ return Transfer.fromPartial(base !== null && base !== void 0 ? base : {});
12381
12528
  },
12382
12529
  fromPartial(object) {
12383
12530
  var _a, _b, _c;
12384
12531
  const message = createBaseTransfer();
12385
- message.destination = (_a = object.destination) !== null && _a !== undefined ? _a : 0;
12386
- message.type = (_b = object.type) !== null && _b !== undefined ? _b : 0;
12387
- message.minTransferTime = (_c = object.minTransferTime) !== null && _c !== undefined ? _c : undefined;
12532
+ message.destination = (_a = object.destination) !== null && _a !== void 0 ? _a : 0;
12533
+ message.type = (_b = object.type) !== null && _b !== void 0 ? _b : 0;
12534
+ message.minTransferTime = (_c = object.minTransferTime) !== null && _c !== void 0 ? _c : undefined;
12388
12535
  return message;
12389
12536
  },
12390
12537
  };
@@ -12400,7 +12547,7 @@ const StopsAdjacency = {
12400
12547
  },
12401
12548
  decode(input, length) {
12402
12549
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12403
- let end = length === undefined ? reader.len : reader.pos + length;
12550
+ const end = length === undefined ? reader.len : reader.pos + length;
12404
12551
  const message = createBaseStopsAdjacency();
12405
12552
  while (reader.pos < end) {
12406
12553
  const tag = reader.uint32();
@@ -12447,12 +12594,12 @@ const StopsAdjacency = {
12447
12594
  return obj;
12448
12595
  },
12449
12596
  create(base) {
12450
- return StopsAdjacency.fromPartial(base !== null && base !== undefined ? base : {});
12597
+ return StopsAdjacency.fromPartial(base !== null && base !== void 0 ? base : {});
12451
12598
  },
12452
12599
  fromPartial(object) {
12453
12600
  var _a;
12454
12601
  const message = createBaseStopsAdjacency();
12455
- message.stops = Object.entries((_a = object.stops) !== null && _a !== undefined ? _a : {}).reduce((acc, [key, value]) => {
12602
+ message.stops = Object.entries((_a = object.stops) !== null && _a !== void 0 ? _a : {}).reduce((acc, [key, value]) => {
12456
12603
  if (value !== undefined) {
12457
12604
  acc[key] = StopsAdjacency_StopAdjacency.fromPartial(value);
12458
12605
  }
@@ -12476,7 +12623,7 @@ const StopsAdjacency_StopAdjacency = {
12476
12623
  },
12477
12624
  decode(input, length) {
12478
12625
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12479
- let end = length === undefined ? reader.len : reader.pos + length;
12626
+ const end = length === undefined ? reader.len : reader.pos + length;
12480
12627
  const message = createBaseStopsAdjacency_StopAdjacency();
12481
12628
  while (reader.pos < end) {
12482
12629
  const tag = reader.uint32();
@@ -12505,31 +12652,31 @@ const StopsAdjacency_StopAdjacency = {
12505
12652
  },
12506
12653
  fromJSON(object) {
12507
12654
  return {
12508
- transfers: globalThis.Array.isArray(object === null || object === undefined ? undefined : object.transfers)
12655
+ transfers: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.transfers)
12509
12656
  ? object.transfers.map((e) => Transfer.fromJSON(e))
12510
12657
  : [],
12511
- routes: globalThis.Array.isArray(object === null || object === undefined ? undefined : object.routes) ? object.routes.map((e) => globalThis.String(e)) : [],
12658
+ routes: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.routes) ? object.routes.map((e) => globalThis.String(e)) : [],
12512
12659
  };
12513
12660
  },
12514
12661
  toJSON(message) {
12515
12662
  var _a, _b;
12516
12663
  const obj = {};
12517
- if ((_a = message.transfers) === null || _a === undefined ? undefined : _a.length) {
12664
+ if ((_a = message.transfers) === null || _a === void 0 ? void 0 : _a.length) {
12518
12665
  obj.transfers = message.transfers.map((e) => Transfer.toJSON(e));
12519
12666
  }
12520
- if ((_b = message.routes) === null || _b === undefined ? undefined : _b.length) {
12667
+ if ((_b = message.routes) === null || _b === void 0 ? void 0 : _b.length) {
12521
12668
  obj.routes = message.routes;
12522
12669
  }
12523
12670
  return obj;
12524
12671
  },
12525
12672
  create(base) {
12526
- return StopsAdjacency_StopAdjacency.fromPartial(base !== null && base !== undefined ? base : {});
12673
+ return StopsAdjacency_StopAdjacency.fromPartial(base !== null && base !== void 0 ? base : {});
12527
12674
  },
12528
12675
  fromPartial(object) {
12529
12676
  var _a, _b;
12530
12677
  const message = createBaseStopsAdjacency_StopAdjacency();
12531
- message.transfers = ((_a = object.transfers) === null || _a === undefined ? undefined : _a.map((e) => Transfer.fromPartial(e))) || [];
12532
- message.routes = ((_b = object.routes) === null || _b === undefined ? undefined : _b.map((e) => e)) || [];
12678
+ message.transfers = ((_a = object.transfers) === null || _a === void 0 ? void 0 : _a.map((e) => Transfer.fromPartial(e))) || [];
12679
+ message.routes = ((_b = object.routes) === null || _b === void 0 ? void 0 : _b.map((e) => e)) || [];
12533
12680
  return message;
12534
12681
  },
12535
12682
  };
@@ -12548,7 +12695,7 @@ const StopsAdjacency_StopsEntry = {
12548
12695
  },
12549
12696
  decode(input, length) {
12550
12697
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12551
- let end = length === undefined ? reader.len : reader.pos + length;
12698
+ const end = length === undefined ? reader.len : reader.pos + length;
12552
12699
  const message = createBaseStopsAdjacency_StopsEntry();
12553
12700
  while (reader.pos < end) {
12554
12701
  const tag = reader.uint32();
@@ -12592,12 +12739,12 @@ const StopsAdjacency_StopsEntry = {
12592
12739
  return obj;
12593
12740
  },
12594
12741
  create(base) {
12595
- return StopsAdjacency_StopsEntry.fromPartial(base !== null && base !== undefined ? base : {});
12742
+ return StopsAdjacency_StopsEntry.fromPartial(base !== null && base !== void 0 ? base : {});
12596
12743
  },
12597
12744
  fromPartial(object) {
12598
12745
  var _a;
12599
12746
  const message = createBaseStopsAdjacency_StopsEntry();
12600
- message.key = (_a = object.key) !== null && _a !== undefined ? _a : "";
12747
+ message.key = (_a = object.key) !== null && _a !== void 0 ? _a : "";
12601
12748
  message.value = (object.value !== undefined && object.value !== null)
12602
12749
  ? StopsAdjacency_StopAdjacency.fromPartial(object.value)
12603
12750
  : undefined;
@@ -12619,7 +12766,7 @@ const ServiceRoute = {
12619
12766
  },
12620
12767
  decode(input, length) {
12621
12768
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12622
- let end = length === undefined ? reader.len : reader.pos + length;
12769
+ const end = length === undefined ? reader.len : reader.pos + length;
12623
12770
  const message = createBaseServiceRoute();
12624
12771
  while (reader.pos < end) {
12625
12772
  const tag = reader.uint32();
@@ -12663,13 +12810,13 @@ const ServiceRoute = {
12663
12810
  return obj;
12664
12811
  },
12665
12812
  create(base) {
12666
- return ServiceRoute.fromPartial(base !== null && base !== undefined ? base : {});
12813
+ return ServiceRoute.fromPartial(base !== null && base !== void 0 ? base : {});
12667
12814
  },
12668
12815
  fromPartial(object) {
12669
12816
  var _a, _b;
12670
12817
  const message = createBaseServiceRoute();
12671
- message.type = (_a = object.type) !== null && _a !== undefined ? _a : 0;
12672
- message.name = (_b = object.name) !== null && _b !== undefined ? _b : "";
12818
+ message.type = (_a = object.type) !== null && _a !== void 0 ? _a : 0;
12819
+ message.name = (_b = object.name) !== null && _b !== void 0 ? _b : "";
12673
12820
  return message;
12674
12821
  },
12675
12822
  };
@@ -12685,7 +12832,7 @@ const ServiceRoutesMap = {
12685
12832
  },
12686
12833
  decode(input, length) {
12687
12834
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12688
- let end = length === undefined ? reader.len : reader.pos + length;
12835
+ const end = length === undefined ? reader.len : reader.pos + length;
12689
12836
  const message = createBaseServiceRoutesMap();
12690
12837
  while (reader.pos < end) {
12691
12838
  const tag = reader.uint32();
@@ -12732,12 +12879,12 @@ const ServiceRoutesMap = {
12732
12879
  return obj;
12733
12880
  },
12734
12881
  create(base) {
12735
- return ServiceRoutesMap.fromPartial(base !== null && base !== undefined ? base : {});
12882
+ return ServiceRoutesMap.fromPartial(base !== null && base !== void 0 ? base : {});
12736
12883
  },
12737
12884
  fromPartial(object) {
12738
12885
  var _a;
12739
12886
  const message = createBaseServiceRoutesMap();
12740
- message.routes = Object.entries((_a = object.routes) !== null && _a !== undefined ? _a : {}).reduce((acc, [key, value]) => {
12887
+ message.routes = Object.entries((_a = object.routes) !== null && _a !== void 0 ? _a : {}).reduce((acc, [key, value]) => {
12741
12888
  if (value !== undefined) {
12742
12889
  acc[key] = ServiceRoute.fromPartial(value);
12743
12890
  }
@@ -12761,7 +12908,7 @@ const ServiceRoutesMap_RoutesEntry = {
12761
12908
  },
12762
12909
  decode(input, length) {
12763
12910
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12764
- let end = length === undefined ? reader.len : reader.pos + length;
12911
+ const end = length === undefined ? reader.len : reader.pos + length;
12765
12912
  const message = createBaseServiceRoutesMap_RoutesEntry();
12766
12913
  while (reader.pos < end) {
12767
12914
  const tag = reader.uint32();
@@ -12805,12 +12952,12 @@ const ServiceRoutesMap_RoutesEntry = {
12805
12952
  return obj;
12806
12953
  },
12807
12954
  create(base) {
12808
- return ServiceRoutesMap_RoutesEntry.fromPartial(base !== null && base !== undefined ? base : {});
12955
+ return ServiceRoutesMap_RoutesEntry.fromPartial(base !== null && base !== void 0 ? base : {});
12809
12956
  },
12810
12957
  fromPartial(object) {
12811
12958
  var _a;
12812
12959
  const message = createBaseServiceRoutesMap_RoutesEntry();
12813
- message.key = (_a = object.key) !== null && _a !== undefined ? _a : "";
12960
+ message.key = (_a = object.key) !== null && _a !== void 0 ? _a : "";
12814
12961
  message.value = (object.value !== undefined && object.value !== null)
12815
12962
  ? ServiceRoute.fromPartial(object.value)
12816
12963
  : undefined;
@@ -12838,7 +12985,7 @@ const Timetable$1 = {
12838
12985
  },
12839
12986
  decode(input, length) {
12840
12987
  const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
12841
- let end = length === undefined ? reader.len : reader.pos + length;
12988
+ const end = length === undefined ? reader.len : reader.pos + length;
12842
12989
  const message = createBaseTimetable();
12843
12990
  while (reader.pos < end) {
12844
12991
  const tag = reader.uint32();
@@ -12904,12 +13051,12 @@ const Timetable$1 = {
12904
13051
  return obj;
12905
13052
  },
12906
13053
  create(base) {
12907
- return Timetable$1.fromPartial(base !== null && base !== undefined ? base : {});
13054
+ return Timetable$1.fromPartial(base !== null && base !== void 0 ? base : {});
12908
13055
  },
12909
13056
  fromPartial(object) {
12910
13057
  var _a;
12911
13058
  const message = createBaseTimetable();
12912
- message.version = (_a = object.version) !== null && _a !== undefined ? _a : "";
13059
+ message.version = (_a = object.version) !== null && _a !== void 0 ? _a : "";
12913
13060
  message.stopsAdjacency = (object.stopsAdjacency !== undefined && object.stopsAdjacency !== null)
12914
13061
  ? StopsAdjacency.fromPartial(object.stopsAdjacency)
12915
13062
  : undefined;
@@ -12954,420 +13101,667 @@ function isSet(value) {
12954
13101
  return value !== null && value !== undefined;
12955
13102
  }
12956
13103
 
12957
- const isLittleEndian = (() => {
12958
- const buffer = new ArrayBuffer(4);
12959
- const view = new DataView(buffer);
12960
- view.setUint32(0, 0x12345678);
12961
- return new Uint8Array(buffer)[0] === 0x78;
12962
- })();
12963
- const STANDARD_ENDIANNESS = true; // true = little-endian
12964
- function uint32ArrayToBytes(array) {
12965
- if (isLittleEndian === STANDARD_ENDIANNESS) {
12966
- return new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
13104
+ /**
13105
+ * A class representing a time as minutes since midnight.
13106
+ */
13107
+ class Time {
13108
+ /**
13109
+ * Gets the infinity time as a Time instance.
13110
+ * This represents a time that is conceptually beyond any real possible time.
13111
+ *
13112
+ * @returns A Time instance representing an "infinity" time.
13113
+ */
13114
+ static infinity() {
13115
+ return new Time(Number.MAX_SAFE_INTEGER);
12967
13116
  }
12968
- // If endianness doesn't match, we need to swap byte order
12969
- const result = new Uint8Array(array.length * 4);
12970
- const view = new DataView(result.buffer);
12971
- for (let i = 0; i < array.length; i++) {
12972
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
12973
- view.setUint32(i * 4, array[i], STANDARD_ENDIANNESS);
13117
+ /**
13118
+ * Gets the midnight time as a Time instance.
13119
+ *
13120
+ * @returns A Time instance representing midnight.
13121
+ */
13122
+ static origin() {
13123
+ return new Time(0);
12974
13124
  }
12975
- return result;
12976
- }
12977
- function bytesToUint32Array(bytes) {
12978
- if (bytes.byteLength % 4 !== 0) {
12979
- throw new Error('Byte array length must be a multiple of 4 to convert to Uint32Array');
13125
+ constructor(minutes) {
13126
+ this.minutesSinceMidnight = minutes;
12980
13127
  }
12981
- // If system endianness matches our standard, we can create a view directly
12982
- if (isLittleEndian === STANDARD_ENDIANNESS) {
12983
- return new Uint32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
13128
+ /**
13129
+ * Creates a Time instance from the number of minutes since midnight.
13130
+ *
13131
+ * @param minutes - The number of minutes since midnight.
13132
+ * @returns A Time instance representing the specified time.
13133
+ */
13134
+ static fromMinutes(minutes) {
13135
+ return new Time(minutes);
12984
13136
  }
12985
- // If endianness doesn't match, we need to swap byte order
12986
- const result = new Uint32Array(bytes.byteLength / 4);
12987
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
12988
- for (let i = 0; i < result.length; i++) {
12989
- result[i] = view.getUint32(i * 4, STANDARD_ENDIANNESS);
13137
+ /**
13138
+ * Creates a Time instance from hours, minutes, and seconds.
13139
+ * Rounds to the closest minute as times are represented in minutes from midnight.
13140
+ *
13141
+ * @param hours - The hours component of the time.
13142
+ * @param minutes - The minutes component of the time.
13143
+ * @param seconds - The seconds component of the time.
13144
+ * @returns A Time instance representing the specified time.
13145
+ */
13146
+ static fromHMS(hours, minutes, seconds) {
13147
+ if (hours < 0 ||
13148
+ minutes < 0 ||
13149
+ seconds < 0 ||
13150
+ minutes >= 60 ||
13151
+ seconds >= 60) {
13152
+ throw new Error('Invalid time. Ensure hours, minutes, and seconds are valid values.');
13153
+ }
13154
+ const totalSeconds = seconds + 60 * minutes + 3600 * hours;
13155
+ const roundedMinutes = Math.round(totalSeconds / 60);
13156
+ return new Time(roundedMinutes);
12990
13157
  }
12991
- return result;
12992
- }
12993
- function uint16ArrayToBytes(array) {
12994
- if (isLittleEndian === STANDARD_ENDIANNESS) {
12995
- return new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
13158
+ /**
13159
+ * Creates a Time instance from hours, minutes.
13160
+ *
13161
+ * @param hours - The hours component of the time.
13162
+ * @param minutes - The minutes component of the time.
13163
+ * @returns A Time instance representing the specified time.
13164
+ */
13165
+ static fromHM(hours, minutes) {
13166
+ if (hours < 0 || minutes < 0 || minutes >= 60) {
13167
+ throw new Error('Invalid time. Ensure hours and minutes are valid values.');
13168
+ }
13169
+ return new Time(minutes + hours * 60);
12996
13170
  }
12997
- // If endianness doesn't match, we need to swap byte order
12998
- const result = new Uint8Array(array.length * 2);
12999
- const view = new DataView(result.buffer);
13000
- for (let i = 0; i < array.length; i++) {
13001
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13002
- view.setUint16(i * 2, array[i], STANDARD_ENDIANNESS);
13171
+ /**
13172
+ * Parses a JavaScript Date object and creates a Time instance.
13173
+ *
13174
+ * @param date - A JavaScript Date object representing the time.
13175
+ * @returns A Time instance representing the parsed time.
13176
+ */
13177
+ static fromDate(date) {
13178
+ const hours = date.getHours();
13179
+ const minutes = date.getMinutes();
13180
+ const seconds = date.getSeconds();
13181
+ return Time.fromHMS(hours, minutes, seconds);
13003
13182
  }
13004
- return result;
13005
- }
13006
- function bytesToUint16Array(bytes) {
13007
- if (bytes.byteLength % 2 !== 0) {
13008
- throw new Error('Byte array length must be a multiple of 2 to convert to Uint16Array');
13183
+ /**
13184
+ * Parses a time string in the format "HH:MM:SS" or "HH:MM" and creates a Time instance.
13185
+ *
13186
+ * @param timeStr - A string representing the time in "HH:MM:SS" or "HH:MM" format.
13187
+ * @returns A Time instance representing the parsed time.
13188
+ */
13189
+ static fromString(timeStr) {
13190
+ const [hoursStr, minutesStr, secondsStr] = timeStr.split(':');
13191
+ if (hoursStr === undefined ||
13192
+ minutesStr === undefined ||
13193
+ hoursStr.trim() === '' ||
13194
+ minutesStr.trim() === '' ||
13195
+ isNaN(Number(hoursStr)) ||
13196
+ isNaN(Number(minutesStr)) ||
13197
+ (secondsStr !== undefined &&
13198
+ (secondsStr.trim() === '' || isNaN(Number(secondsStr))))) {
13199
+ throw new Error('Input string must be in the format "HH:MM:SS" or "HH:MM".');
13200
+ }
13201
+ const hours = parseInt(hoursStr, 10);
13202
+ const minutes = parseInt(minutesStr, 10);
13203
+ const seconds = secondsStr !== undefined ? parseInt(secondsStr, 10) : 0;
13204
+ return Time.fromHMS(hours, minutes, seconds);
13009
13205
  }
13010
- // If system endianness matches our standard, we can create a view directly
13011
- if (isLittleEndian === STANDARD_ENDIANNESS) {
13012
- return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2);
13206
+ /**
13207
+ * Converts the Time instance to a string in "HH:MM:SS" format.
13208
+ *
13209
+ * @returns A string representing the time.
13210
+ */
13211
+ toString() {
13212
+ let hours = Math.floor(this.minutesSinceMidnight / 60);
13213
+ const minutes = Math.floor(this.minutesSinceMidnight % 60);
13214
+ if (hours >= 24) {
13215
+ hours = hours % 24;
13216
+ }
13217
+ return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
13013
13218
  }
13014
- // If endianness doesn't match, we need to swap byte order
13015
- const result = new Uint16Array(bytes.byteLength / 2);
13016
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13017
- for (let i = 0; i < result.length; i++) {
13018
- result[i] = view.getUint16(i * 2, STANDARD_ENDIANNESS);
13219
+ /**
13220
+ * Converts the Time instance to the total number of minutes since midnight, rounded to the closest minute.
13221
+ *
13222
+ * @returns The time in minutes since midnight.
13223
+ */
13224
+ toMinutes() {
13225
+ return this.minutesSinceMidnight;
13019
13226
  }
13020
- return result;
13021
- }
13022
- const serializeStopsAdjacency = (stopsAdjacency) => {
13023
- const protoStopsAdjacency = {
13024
- stops: {},
13025
- };
13026
- stopsAdjacency.forEach((value, key) => {
13027
- protoStopsAdjacency.stops[key] = {
13028
- transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: serializeTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
13029
- minTransferTime: transfer.minTransferTime.toSeconds(),
13030
- })))),
13031
- routes: value.routes,
13032
- };
13033
- });
13034
- return protoStopsAdjacency;
13035
- };
13036
- const serializeRoutesAdjacency = (routesAdjacency) => {
13037
- const protoRoutesAdjacency = {
13038
- routes: {},
13039
- };
13040
- routesAdjacency.forEach((value, key) => {
13041
- protoRoutesAdjacency.routes[key] = {
13042
- stopTimes: uint16ArrayToBytes(value.stopTimes),
13043
- pickUpDropOffTypes: value.pickUpDropOffTypes,
13044
- stops: uint32ArrayToBytes(value.stops),
13045
- serviceRouteId: value.serviceRouteId,
13046
- };
13047
- });
13048
- return protoRoutesAdjacency;
13049
- };
13050
- const serializeServiceRoutesMap = (serviceRoutesMap) => {
13051
- const protoServiceRoutesMap = {
13052
- routes: {},
13053
- };
13054
- serviceRoutesMap.forEach((value, key) => {
13055
- protoServiceRoutesMap.routes[key] = {
13056
- type: serializeRouteType(value.type),
13057
- name: value.name,
13058
- };
13059
- });
13060
- return protoServiceRoutesMap;
13061
- };
13062
- const deserializeStopsAdjacency = (protoStopsAdjacency) => {
13063
- const stopsAdjacency = new Map();
13064
- Object.entries(protoStopsAdjacency.stops).forEach(([keyStr, value]) => {
13065
- const key = parseInt(keyStr, 10);
13066
- stopsAdjacency.set(key, {
13067
- transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: parseTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
13068
- minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
13069
- })))),
13070
- routes: value.routes,
13071
- });
13072
- });
13073
- return stopsAdjacency;
13074
- };
13075
- const deserializeRoutesAdjacency = (protoRoutesAdjacency) => {
13076
- const routesAdjacency = new Map();
13077
- Object.entries(protoRoutesAdjacency.routes).forEach(([key, value]) => {
13078
- const stops = bytesToUint32Array(value.stops);
13079
- const indices = new Map();
13080
- for (let i = 0; i < stops.length; i++) {
13081
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13082
- indices.set(stops[i], i);
13083
- }
13084
- routesAdjacency.set(key, {
13085
- stopTimes: bytesToUint16Array(value.stopTimes),
13086
- pickUpDropOffTypes: value.pickUpDropOffTypes,
13087
- stops: stops,
13088
- stopIndices: indices,
13089
- serviceRouteId: value.serviceRouteId,
13227
+ /**
13228
+ * Adds a Duration to the current Time instance and returns a new Time instance.
13229
+ *
13230
+ * @param duration - A Duration instance representing the duration to add.
13231
+ * @returns A new Time instance with the added duration.
13232
+ */
13233
+ plus(duration) {
13234
+ const totalSeconds = this.minutesSinceMidnight * 60 + duration.toSeconds();
13235
+ return new Time(Math.round(totalSeconds / 60));
13236
+ }
13237
+ /**
13238
+ * Subtracts a Duration from the current Time instance and returns a new Time instance.
13239
+ *
13240
+ * @param duration - A Duration instance representing the duration to subtract.
13241
+ * @returns A new Time instance with the subtracted duration.
13242
+ */
13243
+ minus(duration) {
13244
+ let totalSeconds = this.minutesSinceMidnight * 60 - duration.toSeconds();
13245
+ if (totalSeconds < 0) {
13246
+ totalSeconds += 24 * 3600; // Adjust for negative time to loop back to previous day
13247
+ }
13248
+ return new Time(Math.round(totalSeconds / 60));
13249
+ }
13250
+ /**
13251
+ * Subtracts another Time instance from the current Time instance and returns the Duration.
13252
+ *
13253
+ * @param otherTime - A Time instance representing the time to subtract.
13254
+ * @returns A Duration instance representing the time difference.
13255
+ */
13256
+ diff(otherTime) {
13257
+ const totalMinutes = this.minutesSinceMidnight - otherTime.toMinutes();
13258
+ return Duration.fromSeconds(Math.abs(totalMinutes * 60));
13259
+ }
13260
+ /**
13261
+ * Computes the maximum Time instance among the provided Time instances.
13262
+ *
13263
+ * @param times - An array of Time instances to compare.
13264
+ * @returns A Time instance representing the maximum time.
13265
+ */
13266
+ static max(...times) {
13267
+ if (times.length === 0) {
13268
+ throw new Error('At least one Time instance is required.');
13269
+ }
13270
+ return times.reduce((maxTime, currentTime) => {
13271
+ return currentTime.isAfter(maxTime) ? currentTime : maxTime;
13090
13272
  });
13091
- });
13092
- return routesAdjacency;
13093
- };
13094
- const deserializeServiceRoutesMap = (protoServiceRoutesMap) => {
13095
- const serviceRoutesMap = new Map();
13096
- Object.entries(protoServiceRoutesMap.routes).forEach(([key, value]) => {
13097
- serviceRoutesMap.set(key, {
13098
- type: parseRouteType(value.type),
13099
- name: value.name,
13273
+ }
13274
+ /**
13275
+ * Computes the minimum Time instance among the provided Time instances.
13276
+ *
13277
+ * @param times - An array of Time instances to compare.
13278
+ * @returns A Time instance representing the minimum time.
13279
+ */
13280
+ static min(...times) {
13281
+ if (times.length === 0) {
13282
+ throw new Error('At least one Time instance is required.');
13283
+ }
13284
+ return times.reduce((minTime, currentTime) => {
13285
+ return currentTime.isBefore(minTime) ? currentTime : minTime;
13100
13286
  });
13101
- });
13102
- return serviceRoutesMap;
13103
- };
13104
- const parseTransferType = (type) => {
13105
- switch (type) {
13106
- case TransferType.RECOMMENDED_TRANSFER_POINT:
13107
- return 'RECOMMENDED';
13108
- case TransferType.TIMED_TRANSFER:
13109
- return 'GUARANTEED';
13110
- case TransferType.REQUIRES_MINIMAL_TIME:
13111
- return 'REQUIRES_MINIMAL_TIME';
13112
- case TransferType.IN_SEAT_TRANSFER:
13113
- return 'IN_SEAT';
13114
- case TransferType.UNRECOGNIZED:
13115
- throw new Error('Unrecognized protobuf transfer type.');
13116
13287
  }
13117
- };
13118
- const serializeTransferType = (type) => {
13119
- switch (type) {
13120
- case 'RECOMMENDED':
13121
- return TransferType.RECOMMENDED_TRANSFER_POINT;
13122
- case 'GUARANTEED':
13123
- return TransferType.TIMED_TRANSFER;
13124
- case 'REQUIRES_MINIMAL_TIME':
13125
- return TransferType.REQUIRES_MINIMAL_TIME;
13126
- case 'IN_SEAT':
13127
- return TransferType.IN_SEAT_TRANSFER;
13288
+ /**
13289
+ * Determines if the current Time instance is after another Time instance.
13290
+ *
13291
+ * @param otherTime - A Time instance to compare against.
13292
+ * @returns True if the current Time instance is after the other Time instance, otherwise false.
13293
+ */
13294
+ isAfter(otherTime) {
13295
+ return this.minutesSinceMidnight > otherTime.toMinutes();
13128
13296
  }
13129
- };
13130
- const parseRouteType = (type) => {
13131
- switch (type) {
13132
- case RouteType.TRAM:
13133
- return 'TRAM';
13134
- case RouteType.SUBWAY:
13135
- return 'SUBWAY';
13136
- case RouteType.RAIL:
13137
- return 'RAIL';
13138
- case RouteType.BUS:
13139
- return 'BUS';
13140
- case RouteType.FERRY:
13141
- return 'FERRY';
13142
- case RouteType.CABLE_TRAM:
13143
- return 'CABLE_TRAM';
13144
- case RouteType.AERIAL_LIFT:
13145
- return 'AERIAL_LIFT';
13146
- case RouteType.FUNICULAR:
13147
- return 'FUNICULAR';
13148
- case RouteType.TROLLEYBUS:
13149
- return 'TROLLEYBUS';
13150
- case RouteType.MONORAIL:
13151
- return 'MONORAIL';
13152
- case RouteType.UNRECOGNIZED:
13153
- default:
13154
- throw new Error('Unrecognized protobuf route type.');
13297
+ /**
13298
+ * Determines if the current Time instance is before another Time instance.
13299
+ *
13300
+ * @param otherTime - A Time instance to compare against.
13301
+ * @returns True if the current Time instance is before the other Time instance, otherwise false.
13302
+ */
13303
+ isBefore(otherTime) {
13304
+ return this.minutesSinceMidnight < otherTime.toMinutes();
13155
13305
  }
13156
- };
13157
- const serializeRouteType = (type) => {
13158
- switch (type) {
13159
- case 'TRAM':
13160
- return RouteType.TRAM;
13161
- case 'SUBWAY':
13162
- return RouteType.SUBWAY;
13163
- case 'RAIL':
13164
- return RouteType.RAIL;
13165
- case 'BUS':
13166
- return RouteType.BUS;
13167
- case 'FERRY':
13168
- return RouteType.FERRY;
13169
- case 'CABLE_TRAM':
13170
- return RouteType.CABLE_TRAM;
13171
- case 'AERIAL_LIFT':
13172
- return RouteType.AERIAL_LIFT;
13173
- case 'FUNICULAR':
13174
- return RouteType.FUNICULAR;
13175
- case 'TROLLEYBUS':
13176
- return RouteType.TROLLEYBUS;
13177
- case 'MONORAIL':
13178
- return RouteType.MONORAIL;
13306
+ /**
13307
+ * Determines if the current Time instance is equal to another Time instance.
13308
+ *
13309
+ * @param otherTime - A Time instance to compare against.
13310
+ * @returns True if the current Time instance is equal to the other Time instance, otherwise false.
13311
+ */
13312
+ equals(otherTime) {
13313
+ return this.minutesSinceMidnight === otherTime.toMinutes();
13179
13314
  }
13180
- };
13315
+ }
13181
13316
 
13317
+ const REGULAR = 0;
13318
+ const NOT_AVAILABLE = 1;
13319
+ const MUST_PHONE_AGENCY = 2;
13320
+ const MUST_COORDINATE_WITH_DRIVER = 3;
13321
+ const pickUpDropOffTypeMap = [
13322
+ 'REGULAR',
13323
+ 'NOT_AVAILABLE',
13324
+ 'MUST_PHONE_AGENCY',
13325
+ 'MUST_COORDINATE_WITH_DRIVER',
13326
+ ];
13182
13327
  /**
13183
- * A class representing a time as minutes since midnight.
13328
+ * Converts a numerical representation of a pick-up/drop-off type
13329
+ * into its corresponding string representation.
13330
+ *
13331
+ * @param numericalType - The numerical value representing the pick-up/drop-off type.
13332
+ * @returns The corresponding PickUpDropOffType as a string.
13333
+ * @throws An error if the numerical type is invalid.
13184
13334
  */
13185
- class Time {
13335
+ const toPickupDropOffType = (numericalType) => {
13336
+ const type = pickUpDropOffTypeMap[numericalType];
13337
+ if (!type) {
13338
+ throw new Error(`Invalid pickup/drop-off type ${numericalType}`);
13339
+ }
13340
+ return type;
13341
+ };
13342
+ /**
13343
+ * A route identifies all trips of a given service route sharing the same list of stops.
13344
+ */
13345
+ class Route {
13346
+ constructor(stopTimes, pickUpDropOffTypes, stops, serviceRouteId) {
13347
+ this.stopTimes = stopTimes;
13348
+ this.pickUpDropOffTypes = pickUpDropOffTypes;
13349
+ this.stops = stops;
13350
+ this.serviceRouteId = serviceRouteId;
13351
+ this.nbStops = stops.length;
13352
+ this.nbTrips = this.stopTimes.length / (this.stops.length * 2);
13353
+ this.stopIndices = new Map();
13354
+ for (let i = 0; i < stops.length; i++) {
13355
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13356
+ this.stopIndices.set(stops[i], i);
13357
+ }
13358
+ }
13186
13359
  /**
13187
- * Gets the infinity time as a Time instance.
13188
- * This represents a time that is conceptually beyond any real possible time.
13360
+ * Serializes the Route into binary arrays.
13189
13361
  *
13190
- * @returns A Time instance representing an "infinity" time.
13362
+ * @returns The serialized binary data.
13191
13363
  */
13192
- static infinity() {
13193
- return new Time(Number.MAX_SAFE_INTEGER);
13364
+ serialize() {
13365
+ return {
13366
+ stopTimes: this.stopTimes,
13367
+ pickUpDropOffTypes: this.pickUpDropOffTypes,
13368
+ stops: this.stops,
13369
+ serviceRouteId: this.serviceRouteId,
13370
+ };
13194
13371
  }
13195
13372
  /**
13196
- * Gets the midnight time as a Time instance.
13373
+ * Checks if stop A is before stop B in the route.
13197
13374
  *
13198
- * @returns A Time instance representing midnight.
13375
+ * @param stopA - The StopId of the first stop.
13376
+ * @param stopB - The StopId of the second stop.
13377
+ * @returns True if stop A is before stop B, false otherwise.
13199
13378
  */
13200
- static origin() {
13201
- return new Time(0);
13202
- }
13203
- constructor(minutes) {
13204
- this.minutesSinceMidnight = minutes;
13379
+ isBefore(stopA, stopB) {
13380
+ const stopAIndex = this.stopIndices.get(stopA);
13381
+ if (stopAIndex === undefined) {
13382
+ throw new Error(`Stop index ${stopAIndex} not found in route ${this.serviceRouteId}`);
13383
+ }
13384
+ const stopBIndex = this.stopIndices.get(stopB);
13385
+ if (stopBIndex === undefined) {
13386
+ throw new Error(`Stop index ${stopBIndex} not found in route ${this.serviceRouteId}`);
13387
+ }
13388
+ return stopAIndex < stopBIndex;
13205
13389
  }
13206
13390
  /**
13207
- * Creates a Time instance from the number of minutes since midnight.
13391
+ * Retrieves the number of stops in the route.
13208
13392
  *
13209
- * @param minutes - The number of minutes since midnight.
13210
- * @returns A Time instance representing the specified time.
13393
+ * @returns The total number of stops in the route.
13211
13394
  */
13212
- static fromMinutes(minutes) {
13213
- return new Time(minutes);
13395
+ getNbStops() {
13396
+ return this.nbStops;
13214
13397
  }
13215
13398
  /**
13216
- * Creates a Time instance from hours, minutes, and seconds.
13217
- * Rounds to the closest minute as times are represented in minutes from midnight.
13399
+ * Finds the ServiceRouteId of the route. It corresponds the identifier
13400
+ * of the service shown to the end user as a route.
13218
13401
  *
13219
- * @param hours - The hours component of the time.
13220
- * @param minutes - The minutes component of the time.
13221
- * @param seconds - The seconds component of the time.
13222
- * @returns A Time instance representing the specified time.
13402
+ * @returns The ServiceRouteId of the route.
13223
13403
  */
13224
- static fromHMS(hours, minutes, seconds) {
13225
- if (hours < 0 ||
13226
- minutes < 0 ||
13227
- seconds < 0 ||
13228
- minutes >= 60 ||
13229
- seconds >= 60) {
13230
- throw new Error('Invalid time. Ensure hours, minutes, and seconds are valid values.');
13231
- }
13232
- const totalSeconds = seconds + 60 * minutes + 3600 * hours;
13233
- const roundedMinutes = Math.round(totalSeconds / 60);
13234
- return new Time(roundedMinutes);
13404
+ serviceRoute() {
13405
+ return this.serviceRouteId;
13235
13406
  }
13236
13407
  /**
13237
- * Creates a Time instance from hours, minutes.
13408
+ * Retrieves the arrival time at a specific stop for a given trip.
13238
13409
  *
13239
- * @param hours - The hours component of the time.
13240
- * @param minutes - The minutes component of the time.
13241
- * @returns A Time instance representing the specified time.
13410
+ * @param stopId - The identifier of the stop.
13411
+ * @param tripIndex - The index of the trip.
13412
+ * @returns The arrival time at the specified stop and trip as a Time object.
13242
13413
  */
13243
- static fromHM(hours, minutes) {
13244
- if (hours < 0 || minutes < 0 || minutes >= 60) {
13245
- throw new Error('Invalid time. Ensure hours and minutes are valid values.');
13414
+ arrivalAt(stopId, tripIndex) {
13415
+ const arrivalIndex = (tripIndex * this.stops.length + this.stopIndex(stopId)) * 2;
13416
+ const arrival = this.stopTimes[arrivalIndex];
13417
+ if (arrival === undefined) {
13418
+ throw new Error(`Arrival time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
13246
13419
  }
13247
- return new Time(minutes + hours * 60);
13420
+ return Time.fromMinutes(arrival);
13248
13421
  }
13249
13422
  /**
13250
- * Parses a JavaScript Date object and creates a Time instance.
13423
+ * Retrieves the departure time at a specific stop for a given trip.
13251
13424
  *
13252
- * @param date - A JavaScript Date object representing the time.
13253
- * @returns A Time instance representing the parsed time.
13425
+ * @param stopId - The identifier of the stop.
13426
+ * @param tripIndex - The index of the trip.
13427
+ * @returns The departure time at the specified stop and trip as a Time object.
13254
13428
  */
13255
- static fromDate(date) {
13256
- const hours = date.getHours();
13257
- const minutes = date.getMinutes();
13258
- const seconds = date.getSeconds();
13259
- return Time.fromHMS(hours, minutes, seconds);
13429
+ departureFrom(stopId, tripIndex) {
13430
+ const departureIndex = (tripIndex * this.stops.length + this.stopIndex(stopId)) * 2 + 1;
13431
+ const departure = this.stopTimes[departureIndex];
13432
+ if (departure === undefined) {
13433
+ throw new Error(`Departure time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
13434
+ }
13435
+ return Time.fromMinutes(departure);
13260
13436
  }
13261
13437
  /**
13262
- * Parses a time string in the format "HH:MM:SS" or "HH:MM" and creates a Time instance.
13438
+ * Retrieves the pick-up type for a specific stop and trip.
13263
13439
  *
13264
- * @param timeStr - A string representing the time in "HH:MM:SS" or "HH:MM" format.
13265
- * @returns A Time instance representing the parsed time.
13440
+ * @param stopId - The identifier of the stop.
13441
+ * @param tripIndex - The index of the trip.
13442
+ * @returns The pick-up type at the specified stop and trip.
13266
13443
  */
13267
- static fromString(timeStr) {
13268
- const [hoursStr, minutesStr, secondsStr] = timeStr.split(':');
13269
- if (hoursStr === undefined ||
13270
- minutesStr === undefined ||
13271
- isNaN(Number(hoursStr)) ||
13272
- isNaN(Number(minutesStr)) ||
13273
- (secondsStr !== undefined && isNaN(Number(secondsStr)))) {
13274
- throw new Error('Input string must be in the format "HH:MM:SS" or "HH:MM".');
13444
+ pickUpTypeFrom(stopId, tripIndex) {
13445
+ const globalIndex = tripIndex * this.stops.length + this.stopIndex(stopId);
13446
+ const byteIndex = Math.floor(globalIndex / 2);
13447
+ const isSecondPair = globalIndex % 2 === 1;
13448
+ const byte = this.pickUpDropOffTypes[byteIndex];
13449
+ if (byte === undefined) {
13450
+ throw new Error(`Pick up type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
13275
13451
  }
13276
- const hours = parseInt(hoursStr, 10);
13277
- const minutes = parseInt(minutesStr, 10);
13278
- const seconds = secondsStr !== undefined ? parseInt(secondsStr, 10) : 0;
13279
- return Time.fromHMS(hours, minutes, seconds);
13452
+ const pickUpValue = isSecondPair
13453
+ ? (byte >> 6) & 0x03 // Upper 2 bits for second pair
13454
+ : (byte >> 2) & 0x03; // Bits 2-3 for first pair
13455
+ return toPickupDropOffType(pickUpValue);
13280
13456
  }
13281
13457
  /**
13282
- * Converts the Time instance to a string in "HH:MM:SS" format.
13458
+ * Retrieves the drop-off type for a specific stop and trip.
13283
13459
  *
13284
- * @returns A string representing the time.
13460
+ * @param stopId - The identifier of the stop.
13461
+ * @param tripIndex - The index of the trip.
13462
+ * @returns The drop-off type at the specified stop and trip.
13285
13463
  */
13286
- toString() {
13287
- const hours = Math.floor(this.minutesSinceMidnight / 60);
13288
- const minutes = Math.floor(this.minutesSinceMidnight % 60);
13289
- return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
13464
+ dropOffTypeAt(stopId, tripIndex) {
13465
+ const globalIndex = tripIndex * this.stops.length + this.stopIndex(stopId);
13466
+ const byteIndex = Math.floor(globalIndex / 2);
13467
+ const isSecondPair = globalIndex % 2 === 1;
13468
+ const byte = this.pickUpDropOffTypes[byteIndex];
13469
+ if (byte === undefined) {
13470
+ throw new Error(`Drop off type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
13471
+ }
13472
+ const dropOffValue = isSecondPair
13473
+ ? (byte >> 4) & 0x03 // Bits 4-5 for second pair
13474
+ : byte & 0x03; // Lower 2 bits for first pair
13475
+ return toPickupDropOffType(dropOffValue);
13290
13476
  }
13291
13477
  /**
13292
- * Converts the Time instance to the total number of minutes since midnight, rounded to the closest minute.
13478
+ * Iterates over the stops in the route, starting from an optional specified stop.
13479
+ * If no start stop is provided, the iteration begins from the first stop in the route.
13293
13480
  *
13294
- * @returns The time in minutes since midnight.
13481
+ * @param [startStopId] - (Optional) The StopId of the stop to start the iteration from.
13482
+ * @returns An IterableIterator of StopIds, starting from the specified stop or the first stop.
13483
+ * @throws An error if the specified start stop is not found in the route.
13295
13484
  */
13296
- toMinutes() {
13297
- return this.minutesSinceMidnight;
13485
+ stopsIterator(startStopId) {
13486
+ const startIndex = startStopId !== undefined ? this.stopIndices.get(startStopId) : 0;
13487
+ if (startIndex === undefined) {
13488
+ throw new Error(`Start stop ${startStopId} not found in route ${this.serviceRouteId}`);
13489
+ }
13490
+ function* generator(stops, startIndex) {
13491
+ for (let i = startIndex; i < stops.length; i++) {
13492
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13493
+ yield stops[i];
13494
+ }
13495
+ }
13496
+ return generator(this.stops, startIndex);
13298
13497
  }
13299
13498
  /**
13300
- * Adds a Duration to the current Time instance and returns a new Time instance.
13301
- *
13302
- * @param duration - A Duration instance representing the duration to add.
13303
- * @returns A new Time instance with the added duration.
13499
+ * Finds the earliest trip that can be taken from a specific stop on a given route,
13500
+ * optionally constrained by a latest trip index and a time before which the trip
13501
+ * should not depart.
13502
+ * *
13503
+ * @param stopId - The StopId of the stop where the trip should be found.
13504
+ * @param [after=Time.origin()] - The earliest time after which the trip should depart.
13505
+ * If not provided, searches all available trips.
13506
+ * @param [beforeTrip] - (Optional) The index of the trip before which the search should be constrained.
13507
+ * If not provided, searches all available trips.
13508
+ * @returns The index of the earliest trip meeting the criteria, or undefined if no such trip is found.
13304
13509
  */
13305
- plus(duration) {
13306
- const totalSeconds = this.minutesSinceMidnight * 60 + duration.toSeconds();
13307
- return new Time(Math.round(totalSeconds / 60));
13510
+ findEarliestTrip(stopId, after = Time.origin(), beforeTrip) {
13511
+ const maxTripIndex = beforeTrip !== undefined
13512
+ ? Math.min(beforeTrip - 1, this.nbTrips - 1)
13513
+ : this.nbTrips - 1;
13514
+ if (maxTripIndex < 0) {
13515
+ return undefined;
13516
+ }
13517
+ let earliestTripIndex;
13518
+ let lowTrip = 0;
13519
+ let highTrip = maxTripIndex;
13520
+ while (lowTrip <= highTrip) {
13521
+ const midTrip = Math.floor((lowTrip + highTrip) / 2);
13522
+ const departure = this.departureFrom(stopId, midTrip);
13523
+ const pickUpType = this.pickUpTypeFrom(stopId, midTrip);
13524
+ if ((departure.isAfter(after) || departure.equals(after)) &&
13525
+ pickUpType !== 'NOT_AVAILABLE') {
13526
+ earliestTripIndex = midTrip;
13527
+ highTrip = midTrip - 1;
13528
+ }
13529
+ else {
13530
+ lowTrip = midTrip + 1;
13531
+ }
13532
+ }
13533
+ return earliestTripIndex;
13308
13534
  }
13309
13535
  /**
13310
- * Subtracts a Duration from the current Time instance and returns a new Time instance.
13311
- *
13312
- * @param duration - A Duration instance representing the duration to subtract.
13313
- * @returns A new Time instance with the subtracted duration.
13536
+ * Retrieves the index of a stop within the route.
13537
+ * @param stopId The StopId of the stop to locate in the route.
13538
+ * @returns The index of the stop in the route.
13314
13539
  */
13315
- minus(duration) {
13316
- let totalSeconds = this.minutesSinceMidnight * 60 - duration.toSeconds();
13317
- if (totalSeconds < 0) {
13318
- totalSeconds += 24 * 3600; // Adjust for negative time to loop back to previous day
13540
+ stopIndex(stopId) {
13541
+ const stopIndex = this.stopIndices.get(stopId);
13542
+ if (stopIndex === undefined) {
13543
+ throw new Error(`Stop index for ${stopId} not found in route ${this.serviceRouteId}`);
13319
13544
  }
13320
- return new Time(Math.round(totalSeconds / 60));
13545
+ return stopIndex;
13546
+ }
13547
+ }
13548
+
13549
+ const isLittleEndian = (() => {
13550
+ const buffer = new ArrayBuffer(4);
13551
+ const view = new DataView(buffer);
13552
+ view.setUint32(0, 0x12345678);
13553
+ return new Uint8Array(buffer)[0] === 0x78;
13554
+ })();
13555
+ const STANDARD_ENDIANNESS = true; // true = little-endian
13556
+ const uint32ArrayToBytes = (array) => {
13557
+ if (isLittleEndian === STANDARD_ENDIANNESS) {
13558
+ return new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
13559
+ }
13560
+ // If endianness doesn't match, we need to swap byte order
13561
+ const result = new Uint8Array(array.length * 4);
13562
+ const view = new DataView(result.buffer);
13563
+ for (let i = 0; i < array.length; i++) {
13564
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13565
+ view.setUint32(i * 4, array[i], STANDARD_ENDIANNESS);
13566
+ }
13567
+ return result;
13568
+ };
13569
+ const bytesToUint32Array = (bytes) => {
13570
+ if (bytes.byteLength % 4 !== 0) {
13571
+ throw new Error('Byte array length must be a multiple of 4 to convert to Uint32Array');
13572
+ }
13573
+ // If system endianness matches our standard, we can create a view directly
13574
+ if (isLittleEndian === STANDARD_ENDIANNESS) {
13575
+ return new Uint32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
13576
+ }
13577
+ // If endianness doesn't match, we need to swap byte order
13578
+ const result = new Uint32Array(bytes.byteLength / 4);
13579
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13580
+ for (let i = 0; i < result.length; i++) {
13581
+ result[i] = view.getUint32(i * 4, STANDARD_ENDIANNESS);
13582
+ }
13583
+ return result;
13584
+ };
13585
+ const uint16ArrayToBytes = (array) => {
13586
+ if (isLittleEndian === STANDARD_ENDIANNESS) {
13587
+ return new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
13588
+ }
13589
+ // If endianness doesn't match, we need to swap byte order
13590
+ const result = new Uint8Array(array.length * 2);
13591
+ const view = new DataView(result.buffer);
13592
+ for (let i = 0; i < array.length; i++) {
13593
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13594
+ view.setUint16(i * 2, array[i], STANDARD_ENDIANNESS);
13595
+ }
13596
+ return result;
13597
+ };
13598
+ const bytesToUint16Array = (bytes) => {
13599
+ if (bytes.byteLength % 2 !== 0) {
13600
+ throw new Error('Byte array length must be a multiple of 2 to convert to Uint16Array');
13601
+ }
13602
+ // If system endianness matches our standard, we can create a view directly
13603
+ if (isLittleEndian === STANDARD_ENDIANNESS) {
13604
+ return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2);
13605
+ }
13606
+ // If endianness doesn't match, we need to swap byte order
13607
+ const result = new Uint16Array(bytes.byteLength / 2);
13608
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13609
+ for (let i = 0; i < result.length; i++) {
13610
+ result[i] = view.getUint16(i * 2, STANDARD_ENDIANNESS);
13611
+ }
13612
+ return result;
13613
+ };
13614
+ const serializeStopsAdjacency = (stopsAdjacency) => {
13615
+ const protoStopsAdjacency = {
13616
+ stops: {},
13617
+ };
13618
+ stopsAdjacency.forEach((value, key) => {
13619
+ protoStopsAdjacency.stops[key] = {
13620
+ transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: serializeTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
13621
+ minTransferTime: transfer.minTransferTime.toSeconds(),
13622
+ })))),
13623
+ routes: value.routes,
13624
+ };
13625
+ });
13626
+ return protoStopsAdjacency;
13627
+ };
13628
+ const serializeRoutesAdjacency = (routesAdjacency) => {
13629
+ const protoRoutesAdjacency = {
13630
+ routes: {},
13631
+ };
13632
+ routesAdjacency.forEach((route, key) => {
13633
+ const routeData = route.serialize();
13634
+ protoRoutesAdjacency.routes[key] = {
13635
+ stopTimes: uint16ArrayToBytes(routeData.stopTimes),
13636
+ pickUpDropOffTypes: routeData.pickUpDropOffTypes,
13637
+ stops: uint32ArrayToBytes(routeData.stops),
13638
+ serviceRouteId: routeData.serviceRouteId,
13639
+ };
13640
+ });
13641
+ return protoRoutesAdjacency;
13642
+ };
13643
+ const serializeServiceRoutesMap = (serviceRoutesMap) => {
13644
+ const protoServiceRoutesMap = {
13645
+ routes: {},
13646
+ };
13647
+ serviceRoutesMap.forEach((value, key) => {
13648
+ protoServiceRoutesMap.routes[key] = {
13649
+ type: serializeRouteType(value.type),
13650
+ name: value.name,
13651
+ };
13652
+ });
13653
+ return protoServiceRoutesMap;
13654
+ };
13655
+ const deserializeStopsAdjacency = (protoStopsAdjacency) => {
13656
+ const stopsAdjacency = new Map();
13657
+ Object.entries(protoStopsAdjacency.stops).forEach(([keyStr, value]) => {
13658
+ const key = parseInt(keyStr, 10);
13659
+ stopsAdjacency.set(key, {
13660
+ transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: parseTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
13661
+ minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
13662
+ })))),
13663
+ routes: value.routes,
13664
+ });
13665
+ });
13666
+ return stopsAdjacency;
13667
+ };
13668
+ const deserializeRoutesAdjacency = (protoRoutesAdjacency) => {
13669
+ const routesAdjacency = new Map();
13670
+ Object.entries(protoRoutesAdjacency.routes).forEach(([key, value]) => {
13671
+ const stops = bytesToUint32Array(value.stops);
13672
+ routesAdjacency.set(key, new Route(bytesToUint16Array(value.stopTimes), value.pickUpDropOffTypes, stops, value.serviceRouteId));
13673
+ });
13674
+ return routesAdjacency;
13675
+ };
13676
+ const deserializeServiceRoutesMap = (protoServiceRoutesMap) => {
13677
+ const serviceRoutesMap = new Map();
13678
+ Object.entries(protoServiceRoutesMap.routes).forEach(([key, value]) => {
13679
+ serviceRoutesMap.set(key, {
13680
+ type: parseRouteType(value.type),
13681
+ name: value.name,
13682
+ });
13683
+ });
13684
+ return serviceRoutesMap;
13685
+ };
13686
+ const parseTransferType = (type) => {
13687
+ switch (type) {
13688
+ case TransferType.RECOMMENDED_TRANSFER_POINT:
13689
+ return 'RECOMMENDED';
13690
+ case TransferType.TIMED_TRANSFER:
13691
+ return 'GUARANTEED';
13692
+ case TransferType.REQUIRES_MINIMAL_TIME:
13693
+ return 'REQUIRES_MINIMAL_TIME';
13694
+ case TransferType.IN_SEAT_TRANSFER:
13695
+ return 'IN_SEAT';
13696
+ case TransferType.UNRECOGNIZED:
13697
+ throw new Error('Unrecognized protobuf transfer type.');
13321
13698
  }
13322
- /**
13323
- * Subtracts another Time instance from the current Time instance and returns the Duration.
13324
- *
13325
- * @param otherTime - A Time instance representing the time to subtract.
13326
- * @returns A Duration instance representing the time difference.
13327
- */
13328
- diff(otherTime) {
13329
- const totalMinutes = this.minutesSinceMidnight - otherTime.toMinutes();
13330
- return Duration.fromSeconds(Math.abs(totalMinutes * 60));
13699
+ };
13700
+ const serializeTransferType = (type) => {
13701
+ switch (type) {
13702
+ case 'RECOMMENDED':
13703
+ return TransferType.RECOMMENDED_TRANSFER_POINT;
13704
+ case 'GUARANTEED':
13705
+ return TransferType.TIMED_TRANSFER;
13706
+ case 'REQUIRES_MINIMAL_TIME':
13707
+ return TransferType.REQUIRES_MINIMAL_TIME;
13708
+ case 'IN_SEAT':
13709
+ return TransferType.IN_SEAT_TRANSFER;
13331
13710
  }
13332
- /**
13333
- * Computes the maximum Time instance among the provided Time instances.
13334
- *
13335
- * @param times - An array of Time instances to compare.
13336
- * @returns A Time instance representing the maximum time.
13337
- */
13338
- static max(...times) {
13339
- if (times.length === 0) {
13340
- throw new Error('At least one Time instance is required.');
13341
- }
13342
- return times.reduce((maxTime, currentTime) => {
13343
- return currentTime.toMinutes() > maxTime.toMinutes()
13344
- ? currentTime
13345
- : maxTime;
13346
- });
13711
+ };
13712
+ const parseRouteType = (type) => {
13713
+ switch (type) {
13714
+ case RouteType.TRAM:
13715
+ return 'TRAM';
13716
+ case RouteType.SUBWAY:
13717
+ return 'SUBWAY';
13718
+ case RouteType.RAIL:
13719
+ return 'RAIL';
13720
+ case RouteType.BUS:
13721
+ return 'BUS';
13722
+ case RouteType.FERRY:
13723
+ return 'FERRY';
13724
+ case RouteType.CABLE_TRAM:
13725
+ return 'CABLE_TRAM';
13726
+ case RouteType.AERIAL_LIFT:
13727
+ return 'AERIAL_LIFT';
13728
+ case RouteType.FUNICULAR:
13729
+ return 'FUNICULAR';
13730
+ case RouteType.TROLLEYBUS:
13731
+ return 'TROLLEYBUS';
13732
+ case RouteType.MONORAIL:
13733
+ return 'MONORAIL';
13734
+ case RouteType.UNRECOGNIZED:
13735
+ default:
13736
+ throw new Error('Unrecognized protobuf route type.');
13347
13737
  }
13348
- /**
13349
- * Computes the minimum Time instance among the provided Time instances.
13350
- *
13351
- * @param times - An array of Time instances to compare.
13352
- * @returns A Time instance representing the minimum time.
13353
- */
13354
- static min(...times) {
13355
- if (times.length === 0) {
13356
- throw new Error('At least one Time instance is required.');
13357
- }
13358
- return times.reduce((minTime, currentTime) => {
13359
- return currentTime.toMinutes() < minTime.toMinutes()
13360
- ? currentTime
13361
- : minTime;
13362
- });
13738
+ };
13739
+ const serializeRouteType = (type) => {
13740
+ switch (type) {
13741
+ case 'TRAM':
13742
+ return RouteType.TRAM;
13743
+ case 'SUBWAY':
13744
+ return RouteType.SUBWAY;
13745
+ case 'RAIL':
13746
+ return RouteType.RAIL;
13747
+ case 'BUS':
13748
+ return RouteType.BUS;
13749
+ case 'FERRY':
13750
+ return RouteType.FERRY;
13751
+ case 'CABLE_TRAM':
13752
+ return RouteType.CABLE_TRAM;
13753
+ case 'AERIAL_LIFT':
13754
+ return RouteType.AERIAL_LIFT;
13755
+ case 'FUNICULAR':
13756
+ return RouteType.FUNICULAR;
13757
+ case 'TROLLEYBUS':
13758
+ return RouteType.TROLLEYBUS;
13759
+ case 'MONORAIL':
13760
+ return RouteType.MONORAIL;
13363
13761
  }
13364
- }
13762
+ };
13365
13763
 
13366
- const REGULAR = 0;
13367
- const NOT_AVAILABLE = 1;
13368
- const MUST_PHONE_AGENCY = 2;
13369
- const MUST_COORDINATE_WITH_DRIVER = 3;
13370
- const ALL_TRANSPORT_MODES = [
13764
+ const ALL_TRANSPORT_MODES = new Set([
13371
13765
  'TRAM',
13372
13766
  'SUBWAY',
13373
13767
  'RAIL',
@@ -13378,11 +13772,10 @@ const ALL_TRANSPORT_MODES = [
13378
13772
  'FUNICULAR',
13379
13773
  'TROLLEYBUS',
13380
13774
  'MONORAIL',
13381
- ];
13382
- const CURRENT_VERSION = '0.0.3';
13775
+ ]);
13776
+ const CURRENT_VERSION = '0.0.4';
13383
13777
  /**
13384
- * The internal transit timetable format
13385
- * reuses some GTFS concepts for the sake of simplicity for now.
13778
+ * The internal transit timetable format.
13386
13779
  */
13387
13780
  class Timetable {
13388
13781
  constructor(stopsAdjacency, routesAdjacency, routes) {
@@ -13391,9 +13784,9 @@ class Timetable {
13391
13784
  this.routes = routes;
13392
13785
  }
13393
13786
  /**
13394
- * Serializes the Timetable into a binary protobuf.
13787
+ * Serializes the Timetable into a binary array.
13395
13788
  *
13396
- * @returns {Uint8Array} - The serialized binary data.
13789
+ * @returns The serialized binary data.
13397
13790
  */
13398
13791
  serialize() {
13399
13792
  const protoTimetable = {
@@ -13409,8 +13802,8 @@ class Timetable {
13409
13802
  /**
13410
13803
  * Deserializes a binary protobuf into a Timetable object.
13411
13804
  *
13412
- * @param {Uint8Array} data - The binary data to deserialize.
13413
- * @returns {Timetable} - The deserialized Timetable object.
13805
+ * @param data - The binary data to deserialize.
13806
+ * @returns The deserialized Timetable object.
13414
13807
  */
13415
13808
  static fromData(data) {
13416
13809
  const reader = new BinaryReader(data);
@@ -13426,108 +13819,89 @@ class Timetable {
13426
13819
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13427
13820
  deserializeServiceRoutesMap(protoTimetable.routes));
13428
13821
  }
13429
- getRoutesThroughStop(stopId) {
13430
- const stopAdjacency = this.stopsAdjacency.get(stopId);
13431
- if (!stopAdjacency) {
13432
- return [];
13433
- }
13434
- return stopAdjacency.routes;
13435
- }
13822
+ /**
13823
+ * Retrieves the route associated with the given route ID.
13824
+ *
13825
+ * @param routeId - The ID of the route to be retrieved.
13826
+ * @returns The route corresponding to the provided ID,
13827
+ * or undefined if no such route exists.
13828
+ */
13436
13829
  getRoute(routeId) {
13437
13830
  return this.routesAdjacency.get(routeId);
13438
13831
  }
13832
+ /**
13833
+ * Retrieves all transfer options available at the specified stop.
13834
+ *
13835
+ * @param stopId - The ID of the stop to get transfers for.
13836
+ * @returns An array of transfer options available at the stop.
13837
+ */
13439
13838
  getTransfers(stopId) {
13440
13839
  var _a, _b;
13441
- return (_b = (_a = this.stopsAdjacency.get(stopId)) === null || _a === undefined ? undefined : _a.transfers) !== null && _b !== undefined ? _b : [];
13840
+ return (_b = (_a = this.stopsAdjacency.get(stopId)) === null || _a === void 0 ? void 0 : _a.transfers) !== null && _b !== void 0 ? _b : [];
13841
+ }
13842
+ /**
13843
+ * Retrieves the service route associated with the given route.
13844
+ * A service route refers to a collection of trips that are displayed
13845
+ * to riders as a single service.
13846
+ *
13847
+ * @param route - The route for which the service route is to be retrieved.
13848
+ * @returns The service route corresponding to the provided route.
13849
+ */
13850
+ getServiceRoute(route) {
13851
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13852
+ return this.routes.get(route.serviceRoute());
13853
+ }
13854
+ /**
13855
+ * Finds all routes passing through a stop.
13856
+ *
13857
+ * @param stopId - The ID of the stop to find routes for.
13858
+ * @returns An array of routes passing through the specified stop.
13859
+ */
13860
+ routesPassingThrough(stopId) {
13861
+ const stopData = this.stopsAdjacency.get(stopId);
13862
+ if (!stopData) {
13863
+ return [];
13864
+ }
13865
+ const routes = [];
13866
+ for (const routeId of stopData.routes) {
13867
+ const route = this.routesAdjacency.get(routeId);
13868
+ if (route) {
13869
+ routes.push(route);
13870
+ }
13871
+ }
13872
+ return routes;
13442
13873
  }
13443
13874
  /**
13444
13875
  * Finds routes that are reachable from a set of stop IDs.
13445
13876
  * Also identifies the first stop available to hop on each route among
13446
13877
  * the input stops.
13878
+ *
13879
+ * @param fromStops - The set of stop IDs to find reachable routes from.
13880
+ * @param transportModes - The set of transport modes to consider for reachable routes.
13881
+ * @returns A map of reachable routes to the first stop available to hop on each route.
13447
13882
  */
13448
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
13449
13883
  findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
13450
- var _a;
13451
13884
  const reachableRoutes = new Map();
13452
- for (const stop of fromStops) {
13453
- const validRoutes = (_a = this.stopsAdjacency
13454
- .get(stop)) === null || _a === undefined ? undefined : _a.routes.filter((routeId) => {
13455
- const serviceRoute = this.getServiceRouteFromRouteId(routeId);
13456
- if (!serviceRoute) {
13457
- return false;
13458
- }
13459
- return transportModes.includes(serviceRoute.type);
13885
+ for (const originStop of fromStops) {
13886
+ const validRoutes = this.routesPassingThrough(originStop).filter((route) => {
13887
+ const serviceRoute = this.getServiceRoute(route);
13888
+ return transportModes.has(serviceRoute.type);
13460
13889
  });
13461
- for (const routeId of validRoutes || []) {
13462
- const hopOnStop = reachableRoutes.get(routeId);
13890
+ for (const route of validRoutes) {
13891
+ const hopOnStop = reachableRoutes.get(route);
13463
13892
  if (hopOnStop) {
13464
- // Checks if the existing hop on stop is before the current stop
13465
- const routeStopIndices = this.routesAdjacency.get(routeId).stopIndices;
13466
- const stopIndex = routeStopIndices.get(stop);
13467
- const hopOnStopIndex = routeStopIndices.get(hopOnStop);
13468
- if (stopIndex < hopOnStopIndex) {
13893
+ if (route.isBefore(originStop, hopOnStop)) {
13469
13894
  // if the current stop is before the existing hop on stop, replace it
13470
- reachableRoutes.set(routeId, stop);
13895
+ reachableRoutes.set(route, originStop);
13471
13896
  }
13472
13897
  }
13473
13898
  else {
13474
- reachableRoutes.set(routeId, stop);
13899
+ reachableRoutes.set(route, originStop);
13475
13900
  }
13476
13901
  }
13477
13902
  }
13478
13903
  return reachableRoutes;
13479
13904
  }
13480
- getServiceRouteFromRouteId(routeId) {
13481
- const route = this.routesAdjacency.get(routeId);
13482
- if (!route) {
13483
- console.warn(`Route ${routeId} not found.`);
13484
- return undefined;
13485
- }
13486
- return this.routes.get(route.serviceRouteId);
13487
- }
13488
- getServiceRoute(serviceRouteId) {
13489
- return this.routes.get(serviceRouteId);
13490
- }
13491
- /**
13492
- * Finds the earliest trip that can be taken from a specific stop on a given route,
13493
- * optionally constrained by a latest trip index and a time before which the trip
13494
- * should not depart.
13495
- */
13496
- findEarliestTrip(route, stopId, beforeTrip, after = Time.origin()) {
13497
- const stopIndex = route.stopIndices.get(stopId);
13498
- const stopsNumber = route.stops.length;
13499
- if (beforeTrip === undefined) {
13500
- for (let tripIndex = 0; tripIndex < route.stopTimes.length / stopsNumber; tripIndex++) {
13501
- const stopTimeIndex = tripIndex * stopsNumber + stopIndex;
13502
- const departure = route.stopTimes[stopTimeIndex * 2 + 1];
13503
- const pickUpType = route.pickUpDropOffTypes[stopTimeIndex * 2];
13504
- if (departure >= after.toMinutes() && pickUpType !== NOT_AVAILABLE) {
13505
- return tripIndex;
13506
- }
13507
- }
13508
- return undefined;
13509
- }
13510
- else {
13511
- let earliestTripIndex;
13512
- let earliestDeparture;
13513
- for (let tripIndex = beforeTrip; // ?? route.stopTimes.length / stopsNumber - 1;
13514
- tripIndex >= 0; tripIndex--) {
13515
- const stopTimeIndex = tripIndex * stopsNumber + stopIndex;
13516
- const departure = route.stopTimes[stopTimeIndex * 2 + 1];
13517
- const pickUpType = route.pickUpDropOffTypes[stopTimeIndex * 2];
13518
- if (departure < after.toMinutes()) {
13519
- break;
13520
- }
13521
- if (pickUpType !== NOT_AVAILABLE &&
13522
- (earliestDeparture === undefined ||
13523
- departure < earliestDeparture.toMinutes())) {
13524
- earliestTripIndex = tripIndex;
13525
- earliestDeparture = Time.fromMinutes(departure);
13526
- }
13527
- }
13528
- return earliestTripIndex;
13529
- }
13530
- }
13531
13905
  }
13532
13906
 
13533
13907
  const standardProfile = {
@@ -13844,7 +14218,7 @@ const normalize_options = function (opts) {
13844
14218
  );
13845
14219
  }
13846
14220
  // Normalize option `columns`
13847
- options.cast_first_line_to_header = null;
14221
+ options.cast_first_line_to_header = undefined;
13848
14222
  if (options.columns === true) {
13849
14223
  // Fields in the first line are converted as-is to columns
13850
14224
  options.cast_first_line_to_header = undefined;
@@ -14376,7 +14750,7 @@ const normalize_options = function (opts) {
14376
14750
  // Normalize option `to`
14377
14751
  if (options.to === undefined || options.to === null) {
14378
14752
  options.to = -1;
14379
- } else {
14753
+ } else if (options.to !== -1) {
14380
14754
  if (typeof options.to === "string" && /\d+/.test(options.to)) {
14381
14755
  options.to = parseInt(options.to);
14382
14756
  }
@@ -14395,7 +14769,7 @@ const normalize_options = function (opts) {
14395
14769
  // Normalize option `to_line`
14396
14770
  if (options.to_line === undefined || options.to_line === null) {
14397
14771
  options.to_line = -1;
14398
- } else {
14772
+ } else if (options.to_line !== -1) {
14399
14773
  if (typeof options.to_line === "string" && /\d+/.test(options.to_line)) {
14400
14774
  options.to_line = parseInt(options.to_line);
14401
14775
  }
@@ -14526,10 +14900,14 @@ const transform = function (original_options = {}) {
14526
14900
  this.state.bufBytesStart += bomLength;
14527
14901
  buf = buf.slice(bomLength);
14528
14902
  // Renormalize original options with the new encoding
14529
- this.options = normalize_options({
14903
+ const options = normalize_options({
14530
14904
  ...this.original_options,
14531
14905
  encoding: encoding,
14532
14906
  });
14907
+ // Properties are merged with the existing options instance
14908
+ for (const key in options) {
14909
+ this.options[key] = options[key];
14910
+ }
14533
14911
  // Options will re-evaluate the Buffer with the new encoding
14534
14912
  ({ comment, escape, quote } = this.options);
14535
14913
  break;
@@ -15272,10 +15650,14 @@ const transform = function (original_options = {}) {
15272
15650
  if (skip_records_with_error) {
15273
15651
  this.state.recordHasError = true;
15274
15652
  if (this.options.on_skip !== undefined) {
15275
- this.options.on_skip(
15276
- err,
15277
- raw ? this.state.rawBuffer.toString(encoding) : undefined,
15278
- );
15653
+ try {
15654
+ this.options.on_skip(
15655
+ err,
15656
+ raw ? this.state.rawBuffer.toString(encoding) : undefined,
15657
+ );
15658
+ } catch (err) {
15659
+ return err;
15660
+ }
15279
15661
  }
15280
15662
  // this.emit('skip', err, raw ? this.state.rawBuffer.toString(encoding) : undefined);
15281
15663
  return undefined;
@@ -15472,11 +15854,17 @@ const hashIds = (ids) => {
15472
15854
  * @param stream The CSV stream.
15473
15855
  * @returns A parser from the csv-parse library.
15474
15856
  */
15475
- const parseCsv = (stream) => {
15857
+ const parseCsv = (stream, numericColumns = []) => {
15476
15858
  return stream.pipe(parse({
15477
15859
  delimiter: ',',
15478
15860
  columns: true,
15479
- cast: true,
15861
+ cast: (value, context) => {
15862
+ if (typeof context.column === 'string' &&
15863
+ numericColumns.includes(context.column)) {
15864
+ return Number(value);
15865
+ }
15866
+ return value;
15867
+ },
15480
15868
  bom: true,
15481
15869
  ignore_last_delimiters: true,
15482
15870
  relax_column_count: true,
@@ -15490,11 +15878,11 @@ const parseCsv = (stream) => {
15490
15878
  * @param profile A configuration object defining the specificities of the GTFS feed.
15491
15879
  * @returns A map of all the valid routes.
15492
15880
  */
15493
- const parseRoutes = (routesStream_1, ...args_1) => __awaiter(undefined, [routesStream_1, ...args_1], undefined, function* (routesStream, profile = standardProfile) {
15881
+ const parseRoutes = (routesStream_1, ...args_1) => __awaiter(void 0, [routesStream_1, ...args_1], void 0, function* (routesStream, profile = standardProfile) {
15494
15882
  var _a, e_1, _b, _c;
15495
15883
  const routes = new Map();
15496
15884
  try {
15497
- for (var _d = true, _e = __asyncValues(parseCsv(routesStream)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15885
+ for (var _d = true, _e = __asyncValues(parseCsv(routesStream, ['route_type'])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15498
15886
  _c = _f.value;
15499
15887
  _d = false;
15500
15888
  const rawLine = _c;
@@ -15505,7 +15893,7 @@ const parseRoutes = (routesStream_1, ...args_1) => __awaiter(undefined, [routesS
15505
15893
  continue;
15506
15894
  }
15507
15895
  routes.set(line.route_id, {
15508
- name: line.route_short_name + '',
15896
+ name: line.route_short_name,
15509
15897
  type: routeType,
15510
15898
  });
15511
15899
  }
@@ -15558,13 +15946,23 @@ const weekdays = {
15558
15946
  * @param date The active date.
15559
15947
  * @param calendarStream A readable stream for the GTFS calendar.txt file.
15560
15948
  */
15561
- const parseCalendar = (calendarStream, serviceIds, date) => __awaiter(undefined, undefined, undefined, function* () {
15949
+ const parseCalendar = (calendarStream, serviceIds, date) => __awaiter(void 0, void 0, void 0, function* () {
15562
15950
  var _a, e_1, _b, _c;
15563
15951
  const activeDate = toGtfsDate(date);
15564
15952
  const weekday = date.weekday;
15565
15953
  const weekdayIndex = weekdays[weekday];
15566
15954
  try {
15567
- for (var _d = true, _e = __asyncValues(parseCsv(calendarStream)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15955
+ for (var _d = true, _e = __asyncValues(parseCsv(calendarStream, [
15956
+ 'monday',
15957
+ 'tuesday',
15958
+ 'wednesday',
15959
+ 'thursday',
15960
+ 'friday',
15961
+ 'saturday',
15962
+ 'sunday',
15963
+ 'start_date',
15964
+ 'end_date',
15965
+ ])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15568
15966
  _c = _f.value;
15569
15967
  _d = false;
15570
15968
  const rawLine = _c;
@@ -15595,11 +15993,14 @@ const parseCalendar = (calendarStream, serviceIds, date) => __awaiter(undefined,
15595
15993
  * @param date The active date, in the format "YYYYMMDD".
15596
15994
  * @param calendarDatesStream A readable stream for the GTFS calendar_dates.txt file.
15597
15995
  */
15598
- const parseCalendarDates = (calendarDatesStream, serviceIds, date) => __awaiter(undefined, undefined, undefined, function* () {
15996
+ const parseCalendarDates = (calendarDatesStream, serviceIds, date) => __awaiter(void 0, void 0, void 0, function* () {
15599
15997
  var _a, e_2, _b, _c;
15600
15998
  const activeDate = toGtfsDate(date);
15601
15999
  try {
15602
- for (var _d = true, _e = __asyncValues(parseCsv(calendarDatesStream)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
16000
+ for (var _d = true, _e = __asyncValues(parseCsv(calendarDatesStream, [
16001
+ 'date',
16002
+ 'exception_type',
16003
+ ])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15603
16004
  _c = _f.value;
15604
16005
  _d = false;
15605
16006
  const rawLine = _c;
@@ -15632,17 +16033,21 @@ const parseCalendarDates = (calendarDatesStream, serviceIds, date) => __awaiter(
15632
16033
  * @param stopsStream The readable stream containing the stops data.
15633
16034
  * @return A mapping of stop IDs to corresponding stop details.
15634
16035
  */
15635
- const parseStops = (stopsStream, platformParser) => __awaiter(undefined, undefined, undefined, function* () {
16036
+ const parseStops = (stopsStream, platformParser) => __awaiter(void 0, void 0, void 0, function* () {
15636
16037
  var _a, e_1, _b, _c;
15637
16038
  const parsedStops = new Map();
15638
16039
  let i = 0;
15639
16040
  try {
15640
- for (var _d = true, _e = __asyncValues(parseCsv(stopsStream)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
16041
+ for (var _d = true, _e = __asyncValues(parseCsv(stopsStream, [
16042
+ 'stop_lat',
16043
+ 'stop_lon',
16044
+ 'location_type',
16045
+ ])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15641
16046
  _c = _f.value;
15642
16047
  _d = false;
15643
16048
  const rawLine = _c;
15644
16049
  const line = rawLine;
15645
- const stop = Object.assign({ id: i, sourceStopId: line.stop_id + '', name: line.stop_name, lat: line.stop_lat, lon: line.stop_lon, locationType: line.location_type
16050
+ const stop = Object.assign({ id: i, sourceStopId: line.stop_id, name: line.stop_name, lat: line.stop_lat, lon: line.stop_lon, locationType: line.location_type
15646
16051
  ? parseGtfsLocationType(line.location_type)
15647
16052
  : 'SIMPLE_STOP_OR_PLATFORM', children: [] }, (line.parent_station && { parentSourceId: line.parent_station }));
15648
16053
  if (platformParser) {
@@ -15656,7 +16061,7 @@ const parseStops = (stopsStream, platformParser) => __awaiter(undefined, undefin
15656
16061
  console.info(`Could not parse platform for stop ${line.stop_id}.`);
15657
16062
  }
15658
16063
  }
15659
- parsedStops.set(line.stop_id + '', stop);
16064
+ parsedStops.set(line.stop_id, stop);
15660
16065
  i = i + 1;
15661
16066
  }
15662
16067
  }
@@ -15733,11 +16138,14 @@ const parseGtfsLocationType = (gtfsLocationType) => {
15733
16138
  * @param stopsStream The readable stream containing the stops data.
15734
16139
  * @return A mapping of stop IDs to corresponding stop details.
15735
16140
  */
15736
- const parseTransfers = (transfersStream, stopsMap) => __awaiter(undefined, undefined, undefined, function* () {
16141
+ const parseTransfers = (transfersStream, stopsMap) => __awaiter(void 0, void 0, void 0, function* () {
15737
16142
  var _a, e_1, _b, _c;
15738
16143
  const transfers = new Map();
15739
16144
  try {
15740
- for (var _d = true, _e = __asyncValues(parseCsv(transfersStream)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
16145
+ for (var _d = true, _e = __asyncValues(parseCsv(transfersStream, [
16146
+ 'transfer_type',
16147
+ 'min_transfer_time',
16148
+ ])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15741
16149
  _c = _f.value;
15742
16150
  _d = false;
15743
16151
  const rawLine = _c;
@@ -15762,9 +16170,9 @@ const parseTransfers = (transfersStream, stopsMap) => __awaiter(undefined, undef
15762
16170
  console.info(`Missing minimum transfer time between ${transferEntry.from_stop_id} and ${transferEntry.to_stop_id}.`);
15763
16171
  }
15764
16172
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15765
- const fromStop = stopsMap.get(transferEntry.from_stop_id + '');
16173
+ const fromStop = stopsMap.get(transferEntry.from_stop_id);
15766
16174
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15767
- const toStop = stopsMap.get(transferEntry.to_stop_id + '');
16175
+ const toStop = stopsMap.get(transferEntry.to_stop_id);
15768
16176
  const transfer = Object.assign({ destination: toStop.id, type: parseGtfsTransferType(transferEntry.transfer_type) }, (transferEntry.min_transfer_time && {
15769
16177
  minTransferTime: Duration.fromSeconds(transferEntry.min_transfer_time),
15770
16178
  }));
@@ -15796,6 +16204,86 @@ const parseGtfsTransferType = (gtfsTransferType) => {
15796
16204
  }
15797
16205
  };
15798
16206
 
16207
+ /**
16208
+ * Encodes pickup/drop-off types into a Uint8Array using 2 bits per value.
16209
+ * Layout per byte: [drop_off_1][pickup_1][drop_off_0][pickup_0] for stops 0 and 1
16210
+ */
16211
+ const encodePickUpDropOffTypes = (pickUpTypes, dropOffTypes) => {
16212
+ const stopsCount = pickUpTypes.length;
16213
+ // Each byte stores 2 pickup/drop-off pairs (4 bits each)
16214
+ const arraySize = Math.ceil(stopsCount / 2);
16215
+ const encoded = new Uint8Array(arraySize);
16216
+ for (let i = 0; i < stopsCount; i++) {
16217
+ const byteIndex = Math.floor(i / 2);
16218
+ const isSecondPair = i % 2 === 1;
16219
+ const dropOffType = dropOffTypes[i];
16220
+ const pickUpType = pickUpTypes[i];
16221
+ if (dropOffType !== undefined &&
16222
+ pickUpType !== undefined &&
16223
+ byteIndex < encoded.length) {
16224
+ if (isSecondPair) {
16225
+ // Second pair: upper 4 bits
16226
+ const currentByte = encoded[byteIndex];
16227
+ if (currentByte !== undefined) {
16228
+ encoded[byteIndex] =
16229
+ currentByte | (dropOffType << 4) | (pickUpType << 6);
16230
+ }
16231
+ }
16232
+ else {
16233
+ // First pair: lower 4 bits
16234
+ const currentByte = encoded[byteIndex];
16235
+ if (currentByte !== undefined) {
16236
+ encoded[byteIndex] = currentByte | dropOffType | (pickUpType << 2);
16237
+ }
16238
+ }
16239
+ }
16240
+ }
16241
+ return encoded;
16242
+ };
16243
+ /**
16244
+ * Sorts trips by departure time and creates optimized typed arrays
16245
+ */
16246
+ const finalizeRouteFromBuilder = (builder) => {
16247
+ builder.trips.sort((a, b) => a.firstDeparture - b.firstDeparture);
16248
+ const stopsCount = builder.stops.length;
16249
+ const tripsCount = builder.trips.length;
16250
+ const stopsArray = new Uint32Array(builder.stops);
16251
+ const stopTimesArray = new Uint16Array(stopsCount * tripsCount * 2);
16252
+ const allPickUpTypes = [];
16253
+ const allDropOffTypes = [];
16254
+ for (let tripIndex = 0; tripIndex < tripsCount; tripIndex++) {
16255
+ const trip = builder.trips[tripIndex];
16256
+ if (!trip) {
16257
+ throw new Error(`Missing trip data at index ${tripIndex}`);
16258
+ }
16259
+ const baseIndex = tripIndex * stopsCount * 2;
16260
+ for (let stopIndex = 0; stopIndex < stopsCount; stopIndex++) {
16261
+ const timeIndex = baseIndex + stopIndex * 2;
16262
+ const arrivalTime = trip.arrivalTimes[stopIndex];
16263
+ const departureTime = trip.departureTimes[stopIndex];
16264
+ const pickUpType = trip.pickUpTypes[stopIndex];
16265
+ const dropOffType = trip.dropOffTypes[stopIndex];
16266
+ if (arrivalTime === undefined ||
16267
+ departureTime === undefined ||
16268
+ pickUpType === undefined ||
16269
+ dropOffType === undefined) {
16270
+ throw new Error(`Missing trip data for trip ${tripIndex} at stop ${stopIndex}`);
16271
+ }
16272
+ stopTimesArray[timeIndex] = arrivalTime;
16273
+ stopTimesArray[timeIndex + 1] = departureTime;
16274
+ allDropOffTypes.push(dropOffType);
16275
+ allPickUpTypes.push(pickUpType);
16276
+ }
16277
+ }
16278
+ // Use 2-bit encoding for pickup/drop-off types
16279
+ const pickUpDropOffTypesArray = encodePickUpDropOffTypes(allPickUpTypes, allDropOffTypes);
16280
+ return {
16281
+ serviceRouteId: builder.serviceRouteId,
16282
+ stops: stopsArray,
16283
+ stopTimes: stopTimesArray,
16284
+ pickUpDropOffTypes: pickUpDropOffTypesArray,
16285
+ };
16286
+ };
15799
16287
  /**
15800
16288
  * Parses the trips.txt file from a GTFS feed
15801
16289
  *
@@ -15804,11 +16292,11 @@ const parseGtfsTransferType = (gtfsTransferType) => {
15804
16292
  * @param routeIds A mapping of route IDs to route details.
15805
16293
  * @returns A mapping of trip IDs to corresponding route IDs.
15806
16294
  */
15807
- const parseTrips = (tripsStream, serviceIds, routeIds) => __awaiter(undefined, undefined, undefined, function* () {
16295
+ const parseTrips = (tripsStream, serviceIds, routeIds) => __awaiter(void 0, void 0, void 0, function* () {
15808
16296
  var _a, e_1, _b, _c;
15809
16297
  const trips = new Map();
15810
16298
  try {
15811
- for (var _d = true, _e = __asyncValues(parseCsv(tripsStream)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
16299
+ for (var _d = true, _e = __asyncValues(parseCsv(tripsStream, ['stop_sequence'])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
15812
16300
  _c = _f.value;
15813
16301
  _d = false;
15814
16302
  const rawLine = _c;
@@ -15838,11 +16326,14 @@ const buildStopsAdjacencyStructure = (validStops, routes, transfersMap) => {
15838
16326
  const stopsAdjacency = new Map();
15839
16327
  for (const routeId of routes.keys()) {
15840
16328
  const route = routes.get(routeId);
15841
- for (const stop of route.stops) {
16329
+ if (!route) {
16330
+ throw new Error(`Route ${routeId} not found`);
16331
+ }
16332
+ for (const stop of route.stopsIterator()) {
15842
16333
  if (!stopsAdjacency.get(stop) && validStops.has(stop)) {
15843
16334
  stopsAdjacency.set(stop, { routes: [], transfers: [] });
15844
16335
  }
15845
- (_a = stopsAdjacency.get(stop)) === null || _a === undefined ? undefined : _a.routes.push(routeId);
16336
+ (_a = stopsAdjacency.get(stop)) === null || _a === void 0 ? void 0 : _a.routes.push(routeId);
15846
16337
  }
15847
16338
  }
15848
16339
  for (const [stop, transfers] of transfersMap) {
@@ -15866,15 +16357,15 @@ const buildStopsAdjacencyStructure = (validStops, routes, transfersMap) => {
15866
16357
  * @param validStopIds A set of valid stop IDs.
15867
16358
  * @returns A mapping of route IDs to route details. The routes returned correspond to the set of trips from GTFS that share the same stop list.
15868
16359
  */
15869
- const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) => __awaiter(undefined, undefined, undefined, function* () {
16360
+ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) => __awaiter(void 0, void 0, void 0, function* () {
15870
16361
  var _a, e_2, _b, _c;
15871
16362
  var _d, _e;
15872
16363
  /**
15873
- * Inserts a trip at the right place in the routes adjacency structure.
16364
+ * Adds a trip to the appropriate route builder
15874
16365
  */
15875
16366
  const addTrip = (currentTripId) => {
15876
16367
  const gtfsRouteId = validTripIds.get(currentTripId);
15877
- if (!gtfsRouteId) {
16368
+ if (!gtfsRouteId || stops.length === 0) {
15878
16369
  stops = [];
15879
16370
  arrivalTimes = [];
15880
16371
  departureTimes = [];
@@ -15883,84 +16374,42 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
15883
16374
  return;
15884
16375
  }
15885
16376
  const routeId = `${gtfsRouteId}_${hashIds(stops)}`;
15886
- let route = routes.get(routeId);
15887
- if (!route) {
15888
- const stopsCount = stops.length;
15889
- const stopsArray = new Uint32Array(stops);
15890
- const stopTimesArray = new Uint16Array(stopsCount * 2);
15891
- for (let i = 0; i < stopsCount; i++) {
15892
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15893
- stopTimesArray[i * 2] = arrivalTimes[i];
15894
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15895
- stopTimesArray[i * 2 + 1] = departureTimes[i];
15896
- }
15897
- const pickUpDropOffTypesArray = new Uint8Array(stopsCount * 2);
15898
- for (let i = 0; i < stopsCount; i++) {
15899
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15900
- pickUpDropOffTypesArray[i * 2] = pickUpTypes[i];
15901
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15902
- pickUpDropOffTypesArray[i * 2 + 1] = dropOffTypes[i];
15903
- }
15904
- route = {
16377
+ const firstDeparture = departureTimes[0];
16378
+ if (firstDeparture === undefined) {
16379
+ console.warn(`Empty trip ${currentTripId}`);
16380
+ stops = [];
16381
+ arrivalTimes = [];
16382
+ departureTimes = [];
16383
+ pickUpTypes = [];
16384
+ dropOffTypes = [];
16385
+ return;
16386
+ }
16387
+ let routeBuilder = routeBuilders.get(routeId);
16388
+ if (!routeBuilder) {
16389
+ routeBuilder = {
15905
16390
  serviceRouteId: gtfsRouteId,
15906
- stops: stopsArray,
15907
- stopIndices: new Map(stops.map((stop, i) => [stop, i])),
15908
- stopTimes: stopTimesArray,
15909
- pickUpDropOffTypes: pickUpDropOffTypesArray,
16391
+ stops: [...stops],
16392
+ trips: [],
15910
16393
  };
15911
- routes.set(routeId, route);
16394
+ routeBuilders.set(routeId, routeBuilder);
15912
16395
  for (const stop of stops) {
15913
16396
  validStopIds.add(stop);
15914
16397
  }
15915
16398
  }
15916
- else {
15917
- const tripFirstStopDeparture = departureTimes[0];
15918
- if (tripFirstStopDeparture === undefined) {
15919
- throw new Error(`Empty trip ${currentTripId}`);
15920
- }
15921
- // Find the correct position to insert the new trip
15922
- const stopsCount = stops.length;
15923
- let insertPosition = 0;
15924
- const existingTripsCount = route.stopTimes.length / (stopsCount * 2);
15925
- for (let tripIndex = 0; tripIndex < existingTripsCount; tripIndex++) {
15926
- const currentDeparture = route.stopTimes[tripIndex * stopsCount * 2 + 1];
15927
- if (currentDeparture && tripFirstStopDeparture > currentDeparture) {
15928
- insertPosition = (tripIndex + 1) * stopsCount;
15929
- }
15930
- else {
15931
- break;
15932
- }
15933
- }
15934
- // insert data for the new trip at the right place
15935
- const newStopTimesLength = route.stopTimes.length + stopsCount * 2;
15936
- const newStopTimes = new Uint16Array(newStopTimesLength);
15937
- const newPickUpDropOffTypes = new Uint8Array(newStopTimesLength);
15938
- newStopTimes.set(route.stopTimes.slice(0, insertPosition * 2), 0);
15939
- newPickUpDropOffTypes.set(route.pickUpDropOffTypes.slice(0, insertPosition * 2), 0);
15940
- for (let i = 0; i < stopsCount; i++) {
15941
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15942
- newStopTimes[(insertPosition + i) * 2] = arrivalTimes[i];
15943
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15944
- newStopTimes[(insertPosition + i) * 2 + 1] = departureTimes[i];
15945
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15946
- newPickUpDropOffTypes[(insertPosition + i) * 2] = pickUpTypes[i];
15947
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15948
- newPickUpDropOffTypes[(insertPosition + i) * 2 + 1] = dropOffTypes[i];
15949
- }
15950
- const afterInsertionSlice = route.stopTimes.slice(insertPosition * 2);
15951
- newStopTimes.set(afterInsertionSlice, (insertPosition + stopsCount) * 2);
15952
- const afterInsertionTypesSlice = route.pickUpDropOffTypes.slice(insertPosition * 2);
15953
- newPickUpDropOffTypes.set(afterInsertionTypesSlice, (insertPosition + stopsCount) * 2);
15954
- route.stopTimes = newStopTimes;
15955
- route.pickUpDropOffTypes = newPickUpDropOffTypes;
15956
- }
16399
+ routeBuilder.trips.push({
16400
+ firstDeparture,
16401
+ arrivalTimes: [...arrivalTimes],
16402
+ departureTimes: [...departureTimes],
16403
+ pickUpTypes: [...pickUpTypes],
16404
+ dropOffTypes: [...dropOffTypes],
16405
+ });
15957
16406
  stops = [];
15958
16407
  arrivalTimes = [];
15959
16408
  departureTimes = [];
15960
16409
  pickUpTypes = [];
15961
16410
  dropOffTypes = [];
15962
16411
  };
15963
- const routes = new Map();
16412
+ const routeBuilders = new Map();
15964
16413
  let previousSeq = 0;
15965
16414
  let stops = [];
15966
16415
  let arrivalTimes = [];
@@ -15969,33 +16418,39 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
15969
16418
  let dropOffTypes = [];
15970
16419
  let currentTripId = undefined;
15971
16420
  try {
15972
- for (var _f = true, _g = __asyncValues(parseCsv(stopTimesStream)), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
16421
+ for (var _f = true, _g = __asyncValues(parseCsv(stopTimesStream, ['stop_sequence'])), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
15973
16422
  _c = _h.value;
15974
16423
  _f = false;
15975
16424
  const rawLine = _c;
15976
16425
  const line = rawLine;
15977
16426
  if (line.trip_id === currentTripId && line.stop_sequence <= previousSeq) {
15978
- console.warn(`Stop sequences not increasing for trip ${line.trip_id}.`);
16427
+ console.warn(`Stop sequences not increasing for trip ${line.trip_id}: ${line.stop_sequence} > ${previousSeq}.`);
15979
16428
  continue;
15980
16429
  }
15981
16430
  if (!line.arrival_time && !line.departure_time) {
15982
16431
  console.warn(`Missing arrival or departure time for ${line.trip_id} at stop ${line.stop_id}.`);
15983
16432
  continue;
15984
16433
  }
15985
- if (line.pickup_type === 1 && line.drop_off_type === 1) {
16434
+ if (line.pickup_type === '1' && line.drop_off_type === '1') {
15986
16435
  continue;
15987
16436
  }
15988
16437
  if (currentTripId && line.trip_id !== currentTripId && stops.length > 0) {
15989
16438
  addTrip(currentTripId);
15990
16439
  }
15991
16440
  currentTripId = line.trip_id;
15992
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15993
- stops.push(stopsMap.get(line.stop_id + '').id);
16441
+ const stopData = stopsMap.get(line.stop_id);
16442
+ if (!stopData) {
16443
+ console.warn(`Unknown stop ID: ${line.stop_id}`);
16444
+ continue;
16445
+ }
16446
+ stops.push(stopData.id);
15994
16447
  const departure = (_d = line.departure_time) !== null && _d !== void 0 ? _d : line.arrival_time;
15995
16448
  const arrival = (_e = line.arrival_time) !== null && _e !== void 0 ? _e : line.departure_time;
15996
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
16449
+ if (!arrival || !departure) {
16450
+ console.warn(`Missing time data for ${line.trip_id} at stop ${line.stop_id}`);
16451
+ continue;
16452
+ }
15997
16453
  arrivalTimes.push(toTime(arrival).toMinutes());
15998
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15999
16454
  departureTimes.push(toTime(departure).toMinutes());
16000
16455
  pickUpTypes.push(parsePickupDropOffType(line.pickup_type));
16001
16456
  dropOffTypes.push(parsePickupDropOffType(line.drop_off_type));
@@ -16012,20 +16467,24 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
16012
16467
  if (currentTripId) {
16013
16468
  addTrip(currentTripId);
16014
16469
  }
16015
- return routes;
16470
+ const routesAdjacency = new Map();
16471
+ for (const [routeId, routeBuilder] of routeBuilders) {
16472
+ const routeData = finalizeRouteFromBuilder(routeBuilder);
16473
+ routesAdjacency.set(routeId, new Route(routeData.stopTimes, routeData.pickUpDropOffTypes, routeData.stops, routeData.serviceRouteId));
16474
+ }
16475
+ return routesAdjacency;
16016
16476
  });
16017
16477
  const parsePickupDropOffType = (gtfsType) => {
16018
16478
  switch (gtfsType) {
16019
16479
  default:
16020
- console.warn(`Unknown pickup/drop-off type ${gtfsType}`);
16021
16480
  return REGULAR;
16022
- case 0:
16481
+ case '0':
16023
16482
  return REGULAR;
16024
- case 1:
16483
+ case '1':
16025
16484
  return NOT_AVAILABLE;
16026
- case 2:
16485
+ case '2':
16027
16486
  return MUST_PHONE_AGENCY;
16028
- case 3:
16487
+ case '3':
16029
16488
  return MUST_COORDINATE_WITH_DRIVER;
16030
16489
  }
16031
16490
  };
@@ -16052,55 +16511,75 @@ class GtfsParser {
16052
16511
  * @returns An object containing the timetable and stops map.
16053
16512
  */
16054
16513
  parse(date) {
16055
- return __awaiter(this, undefined, undefined, function* () {
16514
+ return __awaiter(this, void 0, void 0, function* () {
16515
+ log.setLevel('INFO');
16056
16516
  const zip = new StreamZip.async({ file: this.path });
16057
16517
  const entries = yield zip.entries();
16058
16518
  const datetime = DateTime.fromJSDate(date);
16059
16519
  const validServiceIds = new Set();
16060
16520
  const validStopIds = new Set();
16061
16521
  log.info(`Parsing ${STOPS_FILE}`);
16522
+ const stopsStart = performance.now();
16062
16523
  const stopsStream = yield zip.stream(STOPS_FILE);
16063
16524
  const parsedStops = yield parseStops(stopsStream, this.profile.platformParser);
16064
- log.info(`${parsedStops.size} parsed stops.`);
16525
+ const stopsEnd = performance.now();
16526
+ log.info(`${parsedStops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
16065
16527
  if (entries[CALENDAR_FILE]) {
16066
16528
  log.info(`Parsing ${CALENDAR_FILE}`);
16529
+ const calendarStart = performance.now();
16067
16530
  const calendarStream = yield zip.stream(CALENDAR_FILE);
16068
16531
  yield parseCalendar(calendarStream, validServiceIds, datetime);
16069
- log.info(`${validServiceIds.size} valid services.`);
16532
+ const calendarEnd = performance.now();
16533
+ log.info(`${validServiceIds.size} valid services. (${(calendarEnd - calendarStart).toFixed(2)}ms)`);
16070
16534
  }
16071
16535
  if (entries[CALENDAR_DATES_FILE]) {
16072
16536
  log.info(`Parsing ${CALENDAR_DATES_FILE}`);
16537
+ const calendarDatesStart = performance.now();
16073
16538
  const calendarDatesStream = yield zip.stream(CALENDAR_DATES_FILE);
16074
16539
  yield parseCalendarDates(calendarDatesStream, validServiceIds, datetime);
16075
- log.info(`${validServiceIds.size} valid services.`);
16540
+ const calendarDatesEnd = performance.now();
16541
+ log.info(`${validServiceIds.size} valid services. (${(calendarDatesEnd - calendarDatesStart).toFixed(2)}ms)`);
16076
16542
  }
16077
16543
  log.info(`Parsing ${ROUTES_FILE}`);
16544
+ const routesStart = performance.now();
16078
16545
  const routesStream = yield zip.stream(ROUTES_FILE);
16079
16546
  const validGtfsRoutes = yield parseRoutes(routesStream, this.profile);
16080
- log.info(`${validGtfsRoutes.size} valid GTFS routes.`);
16547
+ const routesEnd = performance.now();
16548
+ log.info(`${validGtfsRoutes.size} valid GTFS routes. (${(routesEnd - routesStart).toFixed(2)}ms)`);
16081
16549
  log.info(`Parsing ${TRIPS_FILE}`);
16550
+ const tripsStart = performance.now();
16082
16551
  const tripsStream = yield zip.stream(TRIPS_FILE);
16083
16552
  const trips = yield parseTrips(tripsStream, validServiceIds, validGtfsRoutes);
16084
- log.info(`${trips.size} valid trips.`);
16553
+ const tripsEnd = performance.now();
16554
+ log.info(`${trips.size} valid trips. (${(tripsEnd - tripsStart).toFixed(2)}ms)`);
16085
16555
  let transfers = new Map();
16086
16556
  if (entries[TRANSFERS_FILE]) {
16087
16557
  log.info(`Parsing ${TRANSFERS_FILE}`);
16558
+ const transfersStart = performance.now();
16088
16559
  const transfersStream = yield zip.stream(TRANSFERS_FILE);
16089
16560
  transfers = yield parseTransfers(transfersStream, parsedStops);
16090
- log.info(`${transfers.size} valid transfers.`);
16561
+ const transfersEnd = performance.now();
16562
+ log.info(`${transfers.size} valid transfers. (${(transfersEnd - transfersStart).toFixed(2)}ms)`);
16091
16563
  }
16092
16564
  log.info(`Parsing ${STOP_TIMES_FILE}`);
16565
+ const stopTimesStart = performance.now();
16093
16566
  const stopTimesStream = yield zip.stream(STOP_TIMES_FILE);
16094
16567
  const routesAdjacency = yield parseStopTimes(stopTimesStream, parsedStops, trips, validStopIds);
16095
16568
  const stopsAdjacency = buildStopsAdjacencyStructure(validStopIds, routesAdjacency, transfers);
16096
- log.info(`${routesAdjacency.size} valid unique routes.`);
16569
+ const stopTimesEnd = performance.now();
16570
+ log.info(`${routesAdjacency.size} valid unique routes. (${(stopTimesEnd - stopTimesStart).toFixed(2)}ms)`);
16097
16571
  log.info(`Removing unused stops.`);
16572
+ const indexStopsStart = performance.now();
16098
16573
  const stops = indexStops(parsedStops, validStopIds);
16099
- log.info(`${stops.size} used stop stops, ${parsedStops.size - stops.size} unused.`);
16574
+ const indexStopsEnd = performance.now();
16575
+ log.info(`${stops.size} used stop stops, ${parsedStops.size - stops.size} unused. (${(indexStopsEnd - indexStopsStart).toFixed(2)}ms)`);
16100
16576
  yield zip.close();
16101
16577
  const timetable = new Timetable(stopsAdjacency, routesAdjacency, validGtfsRoutes);
16102
16578
  log.info(`Building stops index.`);
16579
+ const stopsIndexStart = performance.now();
16103
16580
  const stopsIndex = new StopsIndex(stops);
16581
+ const stopsIndexEnd = performance.now();
16582
+ log.info(`Stops index built. (${(stopsIndexEnd - stopsIndexStart).toFixed(2)}ms)`);
16104
16583
  log.info('Parsing complete.');
16105
16584
  return { timetable, stopsIndex };
16106
16585
  });
@@ -16113,12 +16592,14 @@ class GtfsParser {
16113
16592
  * @returns An object containing the timetable and stops map.
16114
16593
  */
16115
16594
  parseStops() {
16116
- return __awaiter(this, undefined, undefined, function* () {
16595
+ return __awaiter(this, void 0, void 0, function* () {
16117
16596
  const zip = new StreamZip.async({ file: this.path });
16118
16597
  log.info(`Parsing ${STOPS_FILE}`);
16598
+ const stopsStart = performance.now();
16119
16599
  const stopsStream = yield zip.stream(STOPS_FILE);
16120
16600
  const stops = indexStops(yield parseStops(stopsStream, this.profile.platformParser));
16121
- log.info(`${stops.size} parsed stops.`);
16601
+ const stopsEnd = performance.now();
16602
+ log.info(`${stops.size} parsed stops. (${(stopsEnd - stopsStart).toFixed(2)}ms)`);
16122
16603
  yield zip.close();
16123
16604
  return new StopsIndex(stops);
16124
16605
  });
@@ -16131,7 +16612,7 @@ class GtfsParser {
16131
16612
  * @returns The platform corresponding to this stop.
16132
16613
  */
16133
16614
  const platformParser = (stopEntry) => {
16134
- const stopId = String(stopEntry.stop_id);
16615
+ const stopId = stopEntry.stop_id;
16135
16616
  const stopParts = stopId.split(':');
16136
16617
  if (stopParts.length > 2) {
16137
16618
  return stopParts[2];